-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* This generates files which facilitate creating C libraries from Julia code, using PackageCompiler.jl.
- Loading branch information
Showing
13 changed files
with
431 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ export | |
License, | ||
Logo, | ||
NoDeploy, | ||
PackageCompilerLib, | ||
ProjectFile, | ||
Readme, | ||
RegisterAction, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
using PkgTemplates: @plugin, @with_kw_noshow, Plugin | ||
|
||
# Used to generate the library name | ||
camel_to_snake_case(str::AbstractString) = replace(str, r"([a-z])([A-Z]+)" => s"\1_\2") |> lowercase | ||
|
||
""" | ||
PackageCompilerLib(; | ||
lib_name=nothing, | ||
build_jl="$(contractuser(default_file("build", "build.jl")))", | ||
generate_precompile_jl="$(contractuser(default_file("build", "generate_precompile.jl")))", | ||
additional_precompile_jl="$(contractuser(default_file("build", "additional_precompile.jl")))", | ||
install_sh="$(contractuser(default_file("build", "install.sh")))", | ||
install_txt="$(contractuser(default_file("build", "INSTALL.txt")))", | ||
lib_h="$(contractuser(default_file("build", "lib.h")))", | ||
project_toml="$(contractuser(default_file("build", "Project.toml")))", | ||
makefile="$(contractuser(default_file("Makefile")))", | ||
additional_gitignore=[], | ||
) | ||
Adds files which facilitate the creation of a C-library from the generated project. | ||
See [PackageCompiler.jl](https://github.com/JuliaLang/PackageCompiler.jl) for more information. | ||
## Keyword Arguments | ||
- `lib_name::Union{String, Nothing}`: Name of the library to generate. If `nothing`, | ||
defaults to the snake-case version of the package name. | ||
- `build_jl::String`: The file used to generate the C library. Calls out to `PackageCompiler.jl`. | ||
- `generate_precompile_jl::String`: File with a code which will be used to generate precompile statements. | ||
- `additional_precompile_jl::String`: File with additional precompile statements. | ||
- `install_sh::String`: Installation script for the generated library. | ||
- `install_txt::String`: Installation instructions for the generated library. | ||
- `lib_h::String`: C header file for the generated library. | ||
- `project_toml::String`: Julia `Project.toml` for the build directory. | ||
- `makefile::String`: Makefile with targets to help build the C library. | ||
- `additional_gitignore::Vector{String}`: Additional strings to add to .gitignore. | ||
""" | ||
@plugin struct PackageCompilerLib <: Plugin | ||
lib_name::Union{String, Nothing} = nothing | ||
build_jl::String = default_file("build", "build.jl") | ||
generate_precompile_jl::String = default_file("build", "generate_precompile.jl") | ||
additional_precompile_jl::String = default_file("build", "additional_precompile.jl") | ||
install_sh::String = default_file("build", "install.sh") | ||
install_txt::String = default_file("build", "INSTALL.txt") | ||
lib_h::String = default_file("build", "lib.h") | ||
project_toml::String = default_file("build", "Project.toml") | ||
makefile::String = default_file("Makefile") | ||
additional_gitignore::Vector{String} = [] | ||
end | ||
|
||
function validate(p::PackageCompilerLib, ::Template) | ||
isfile(p.build_jl) || throw(ArgumentError("PackageCompilerLib: $(p.build_jl) does not exist")) | ||
isfile(p.additional_precompile_jl) || throw(ArgumentError("PackageCompilerLib: $(p.additional_precompile_jl) does not exist")) | ||
isfile(p.generate_precompile_jl) || throw(ArgumentError("PackageCompilerLib: $(p.generate_precompile_jl) does not exist")) | ||
isfile(p.install_sh) || throw(ArgumentError("PackageCompilerLib: $(p.install_sh) does not exist")) | ||
isfile(p.install_txt) || throw(ArgumentError("PackageCompilerLib: $(p.install_txt) does not exist")) | ||
isfile(p.lib_h) || throw(ArgumentError("PackageCompilerLib: $(p.lib_h) does not exist")) | ||
isfile(p.project_toml) || throw(ArgumentError("PackageCompilerLib: $(p.project_toml) does not exist")) | ||
isfile(p.makefile) || throw(ArgumentError("PackageCompilerLib: $(p.makefile) does not exist")) | ||
end | ||
|
||
view(p::PackageCompilerLib, t::Template, pkg::AbstractString) = Dict( | ||
"PKG" => pkg, | ||
"LIB" => lib_name(p, pkg), | ||
) | ||
|
||
function lib_name(p::PackageCompilerLib, pkg::AbstractString) | ||
p.lib_name !== nothing ? p.lib_name : camel_to_snake_case(pkg) | ||
end | ||
|
||
function gitignore(p::PackageCompilerLib) | ||
ignore_files = ["build/Manifest.toml", "target"] | ||
append!(ignore_files, p.additional_gitignore) | ||
return ignore_files | ||
end | ||
|
||
function prehook(p::PackageCompilerLib, t::Template, pkg_dir::AbstractString) | ||
# The library name and version are used as the default Makefile output target | ||
# (e.g. the library is built under mylib-0.1.0/). | ||
# If we use a the default library name, p.lib_name === nothing, then the | ||
# gitignore() function won't have access to the default library name. | ||
# To work around this, we get the library name and store the output target directory | ||
# glob here in the prehook, so it can be added by gitignore() later. | ||
pkg = basename(pkg_dir) | ||
library_name = lib_name(p, pkg) | ||
push!(p.additional_gitignore, "/$(library_name)-*") | ||
end | ||
|
||
function hook(p::PackageCompilerLib, t::Template, pkg_dir::AbstractString) | ||
build_dir = joinpath(pkg_dir, "build") | ||
pkg = basename(pkg_dir) | ||
library_name = lib_name(p, pkg) | ||
|
||
build_jl = render_file(p.build_jl, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(build_dir, "build.jl"), build_jl) | ||
|
||
additional_precompile_jl = render_file(p.additional_precompile_jl, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(build_dir, "additional_precompile.jl"), additional_precompile_jl) | ||
|
||
generate_precompile_jl = render_file(p.generate_precompile_jl, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(build_dir, "generate_precompile.jl"), generate_precompile_jl) | ||
|
||
install_sh = render_file(p.install_sh, combined_view(p, t, pkg), tags(p)) | ||
install_sh_target = joinpath(build_dir, "install.sh") | ||
gen_file(install_sh_target, install_sh) | ||
chmod(install_sh_target, 0o755) | ||
|
||
install_txt = render_file(p.install_txt, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(build_dir, "INSTALL.txt"), install_txt) | ||
|
||
lib_h = render_file(p.lib_h, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(build_dir, "$(library_name).h"), lib_h) | ||
|
||
project_toml = render_file(p.project_toml, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(build_dir, "Project.toml"), project_toml) | ||
|
||
makefile = render_file(p.makefile, combined_view(p, t, pkg), tags(p)) | ||
gen_file(joinpath(pkg_dir, "Makefile"), makefile) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
.PHONY: build clean dist instantiate install uninstall | ||
|
||
ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) | ||
BUILD := $(ROOT_DIR)/build | ||
|
||
JULIA ?= julia | ||
JULIA_DIR := $(shell $(JULIA) --startup-file=no -e 'print(dirname(Sys.BINDIR))') | ||
DLEXT := $(shell $(JULIA) --startup-file=no -e 'using Libdl; print(Libdl.dlext)') | ||
VERSION := $(shell sed -n 's/^version *= *"\(.*\)"/\1/p' $(ROOT_DIR)/Project.toml) | ||
OS := $(shell uname) | ||
DEPS := $(shell find . build src -maxdepth 1 -and \( -name \*.toml -or -name \*.jl -or -name Makefile \) -and -not -type l) | ||
|
||
NAME := {{{LIB}}} | ||
NAME_VERSION := $(NAME)-$(VERSION) | ||
_TAR_GZ := $(NAME_VERSION)-$(OS).tar.gz | ||
|
||
DEST_DIR ?= $(NAME_VERSION) | ||
DEST_BASENAME := $(shell basename $(DEST_DIR)) | ||
TAR_GZ := $(abspath $(DEST_DIR)/../$(_TAR_GZ)) | ||
OUT_DIR := $(DEST_DIR)/$(NAME) | ||
BIN_DIR := $(OUT_DIR)/bin | ||
INCLUDE_DIR = $(OUT_DIR)/include | ||
LIB_DIR := $(OUT_DIR)/lib | ||
PREFIX ?= $${HOME}/.local | ||
|
||
LIB_NAME := lib$(NAME).$(DLEXT) | ||
INCLUDES = $(INCLUDE_DIR)/julia_init.h $(INCLUDE_DIR)/$(NAME).h | ||
LIB_PATH := $(LIB_DIR)/$(LIB_NAME) | ||
|
||
.DEFAULT_GOAL := build | ||
|
||
$(LIB_PATH) $(INCLUDES): $(BUILD)/build.jl $(DEPS) | ||
$(JULIA) --startup-file=no --project=. -e 'using Pkg; Pkg.instantiate()' | ||
# The line below should be removed once https://github.com/JuliaLang/PackageCompiler.jl/pull/490 is merged | ||
# (and a new version of PackageCompiler.jl is released). | ||
$(JULIA) --startup-file=no --project=$(BUILD) -e 'import Pkg; Pkg.add(url="https://github.com/kmsquire/PackageCompiler.jl.git", rev="kms/create_library")' | ||
$(JULIA) --startup-file=no --project=$(BUILD) -e 'using Pkg; Pkg.instantiate()' | ||
$(JULIA) --startup-file=no --project=$(BUILD) $< $(OUT_DIR) | ||
# Replace the previous line with the line below to enable verbose debugging during package build | ||
# JULIA_DEBUG=PackageCompiler $(JULIA) --startup-file=no --project=$(BUILD) $< $(OUT_DIR) | ||
|
||
build: $(LIB_PATH) $(INCLUDES) README.md $(BUILD)/INSTALL.txt $(BUILD)/install.sh | ||
cp README.md $(BUILD)/INSTALL.txt $(BUILD)/install.sh $(DEST_DIR) | ||
cd $(DEST_DIR) && ln -sf install.sh uninstall.sh | ||
|
||
install: build | ||
cd $(DEST_DIR) && PREFIX=$(PREFIX) ./install.sh | ||
|
||
uninstall: build | ||
cd $(DEST_DIR) && ./uninstall.sh | ||
|
||
$(TAR_GZ): $(LIB_PATH) $(INCLUDES) Project.toml Manifest.toml $(BUILD)/*.jl $(BUILD)/*.toml | ||
cd $(DEST_DIR)/.. && tar -zcf $(TAR_GZ) \ | ||
$(DEST_BASENAME)/README.md \ | ||
$(DEST_BASENAME)/INSTALL.txt \ | ||
$(DEST_BASENAME)/install.sh \ | ||
$(DEST_BASENAME)/uninstall.sh \ | ||
$(DEST_BASENAME)/$(NAME) | ||
|
||
dist: $(TAR_GZ) | ||
|
||
instantiate: | ||
$(JULIA) --startup-file=no --project=. -e "import Pkg; Pkg.instantiate()" | ||
$(JULIA) --startup-file=no --project=build -e "import Pkg; Pkg.instantiate()" | ||
|
||
clean: | ||
$(RM) -Rf $(OUT_DIR) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
|
||
By default, install.sh installs files under $HOME/.local. | ||
|
||
To install, optionally set the following environment variables and run | ||
install.sh. | ||
|
||
NAME project name (default: {{{LIB}}}) | ||
SOURCE_DIR directory whose contents to copy (default: {{{LIB}}}) | ||
PREFIX destination prefix (default: $HOME/.local) | ||
|
||
Examples: | ||
|
||
$ ./install.sh # install in ~/.local | ||
$ SOURCE_DIR={{{LIB}}} install.sh # install in ~/.local ({{{LIB}}} is the default) | ||
$ PREFIX=/usr/local ./install.sh # install directly in /usr/local (no symlinks) | ||
|
||
To uninstall, make sure PREFIX and NAME have the same values used during | ||
install and run uninstall.sh. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[deps] | ||
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d" | ||
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
## Add manual precompile statements here | ||
|
||
# precompile(Tuple{typeof({{{PKG}}}.increment64), Clong}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import PackageCompiler, TOML | ||
|
||
if length(ARGS) < 1 || length(ARGS) > 2 | ||
println("Usage: julia $PROGRAM_FILE target_dir [major|minor]") | ||
println() | ||
println("where:") | ||
println(" target_dir is the directory to use to create the library bundle") | ||
println(" [major|minor] is the (optional) compatibility version (default: major).") | ||
println(" Use 'minor' if you use new/non-backwards-compatible functionality.") | ||
println() | ||
println("[major|minor] is only useful on OSX.") | ||
exit(1) | ||
end | ||
|
||
const build_dir = @__DIR__ | ||
const target_dir = ARGS[1] | ||
const project_toml = realpath(joinpath(build_dir, "..", "Project.toml")) | ||
const version = VersionNumber(TOML.parsefile(project_toml)["version"]) | ||
|
||
const compatibility = length(ARGS) == 2 ? ARGS[2] : "major" | ||
|
||
PackageCompiler.create_library(".", target_dir; | ||
lib_name="{{{LIB}}}", | ||
precompile_execution_file=[joinpath(build_dir, "generate_precompile.jl")], | ||
precompile_statements_file=[joinpath(build_dir, "additional_precompile.jl")], | ||
incremental=false, | ||
filter_stdlibs=true, | ||
header_files = [joinpath(build_dir, "{{{LIB}}}.h")], | ||
force=true, | ||
version=version, | ||
compat_level=compatibility, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
## Add code to generate precompile statements here | ||
|
||
using {{{PKG}}} | ||
|
||
# function count_to_ten() | ||
# count = zero(Int32) | ||
# while count < 10 | ||
# count = increment32(count) | ||
# end | ||
# end | ||
|
||
# count_to_ten() |
Oops, something went wrong.