From 6f8a516d3edf8389c9cefc72f29d08d597879703 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Wed, 17 Apr 2024 09:43:27 -0400 Subject: [PATCH] Julia: replace LazyArtifacts with manual download (#227) * Replace LazyArtifacts with a manual download * Add missing R warning * More symlink munging * Update docs * Fix argument ordering --- .github/workflows/release.yaml | 8 +---- R/R/compile.R | 6 ++++ docs/internals/development.rst | 6 +++- docs/languages/julia.md | 12 ++++--- julia/Artifacts.toml | 7 ---- julia/Project.toml | 16 +++++++-- julia/docs/src/julia.md | 2 +- julia/src/BridgeStan.jl | 3 +- julia/src/compile.jl | 50 ++++++++++++++++++--------- julia/src/download.jl | 63 ++++++++++++++++++++++++++++++++++ julia/updateArtifacts.jl | 28 --------------- 11 files changed, 131 insertions(+), 70 deletions(-) delete mode 100644 julia/Artifacts.toml create mode 100644 julia/src/download.jl delete mode 100644 julia/updateArtifacts.jl diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 896c68b2..f42b8324 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -56,13 +56,7 @@ jobs: - name: Create tarball run: | - tar --exclude-vcs -chzvf bridgestan-${{ inputs.new_version }}.tar.gz --transform 's,^,bridgestan-${{ inputs.new_version }}/,' * - - # Note: because of the order of operations here, the Julia package inside the tarball can never point to itself. - - name: Update Julia artifact - if: ${{ !inputs.is_rerun }} - run: | - julia ./julia/updateArtifacts.jl bridgestan-${{ inputs.new_version }}.tar.gz "v${{ inputs.new_version }}" + tar --exclude-vcs --hard-dereference -chzvf bridgestan-${{ inputs.new_version }}.tar.gz --transform 's,^,bridgestan-${{ inputs.new_version }}/,' * - name: Setup git identity run: | diff --git a/R/R/compile.R b/R/R/compile.R index 02a18ca7..306dc135 100644 --- a/R/R/compile.R +++ b/R/R/compile.R @@ -48,6 +48,12 @@ get_bridgestan_path <- function() { " to ", path)) get_bridgestan_src() }) + num_files <- length(list.files(HOME_BRIDGESTAN)) + if (num_files >= 5) { + warning(paste0("Found ", num_files, " different versions of BridgeStan in ", + HOME_BRIDGESTAN, ". Consider deleting old versions to save space.")) + } + print("Done!") } return(path) diff --git a/docs/internals/development.rst b/docs/internals/development.rst index bd86c4aa..09b644e5 100644 --- a/docs/internals/development.rst +++ b/docs/internals/development.rst @@ -41,7 +41,11 @@ Julia * Julia dependencies: - * `LazyArtifacts `_ + * `Tar `_ (standard library) + * `TOML `_ (standard library) + * `Downloads `_ (standard library) + * `Inflate.jl `_ (external) + * Julia code is formatted using `JuliaFormatter `_. diff --git a/docs/languages/julia.md b/docs/languages/julia.md index 2bbeffeb..326caecb 100644 --- a/docs/languages/julia.md +++ b/docs/languages/julia.md @@ -75,7 +75,7 @@ from the BridgeStan folder. To use the BridgeStan source you've manually downloaded instead of one the package will download for you, you must use [`set_bridgestan_path()`](BridgeStan.set_bridgestan_path!) or the `$BRIDGESTAN` environment variable. -Note that the Julia package depends on Julia 1.8+. +Note that the Julia package depends on Julia 1.6+ and the `Inflate` package. @@ -526,7 +526,7 @@ Run BridgeStan’s Makefile on a `.stan` file, creating the `.so` used by StanMo This function checks that the path to BridgeStan is valid and will error if it is not. This can be set with `set_bridgestan_path!()`. -source
+source
# **`BridgeStan.get_bridgestan_path`** — *Function*. @@ -539,10 +539,12 @@ get_bridgestan_path() -> String Return the path the the BridgeStan directory. -If the environment variable `BRIDGESTAN` is set, this will be returned. Otherwise, this function downloads an artifact containing the BridgeStan repository and returns the path to the extracted directory. +If the environment variable `BRIDGESTAN` is set, this will be returned. Otherwise, this function downloads a matching version of BridgeStan under a folder called `.bridgestan` in the user's home directory. +See `set_bridgestan_path!()` to set the path from within Julia. -source
+ +source
# **`BridgeStan.set_bridgestan_path!`** — *Function*. @@ -556,5 +558,5 @@ set_bridgestan_path!(path) Set the path BridgeStan. -source
+source
diff --git a/julia/Artifacts.toml b/julia/Artifacts.toml deleted file mode 100644 index 751fbdd4..00000000 --- a/julia/Artifacts.toml +++ /dev/null @@ -1,7 +0,0 @@ -[bridgestan] -git-tree-sha1 = "b390d174e9b46a0756360e7901000adbbcbc9da3" -lazy = true - - [[bridgestan.download]] - sha256 = "33b43753413d6c93396b51bc25c998f176e86855c6c9a6e0f57268d187a12b99" - url = "https://github.com/roualdes/bridgestan/releases/download/v2.4.0/bridgestan-2.4.0.tar.gz" diff --git a/julia/Project.toml b/julia/Project.toml index c8c41061..7c215488 100644 --- a/julia/Project.toml +++ b/julia/Project.toml @@ -1,11 +1,21 @@ name = "BridgeStan" uuid = "c88b6f0a-829e-4b0b-94b7-f06ab5908f5a" -authors = ["Brian Ward ", "Bob Carpenter ", "Edward Roualdes "] +authors = [ + "Brian Ward ", + "Bob Carpenter ", + "Edward Roualdes ", +] version = "2.4.0" [deps] -LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +Inflate = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" +TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" [compat] +Downloads = "1" +TOML = "1" +Inflate = "0.1" +Tar = "1" julia = "1.6" -LazyArtifacts = "1" diff --git a/julia/docs/src/julia.md b/julia/docs/src/julia.md index 451f041a..202ae636 100644 --- a/julia/docs/src/julia.md +++ b/julia/docs/src/julia.md @@ -51,7 +51,7 @@ one the package will download for you, you must use [`set_bridgestan_path()`](BridgeStan.set_bridgestan_path!) or the `$BRIDGESTAN` environment variable. -Note that the Julia package depends on Julia 1.8+. +Note that the Julia package depends on Julia 1.6+ and the `Inflate` package. ## Example Program diff --git a/julia/src/BridgeStan.jl b/julia/src/BridgeStan.jl index f1a2a58a..9cc04927 100644 --- a/julia/src/BridgeStan.jl +++ b/julia/src/BridgeStan.jl @@ -1,7 +1,5 @@ module BridgeStan -using LazyArtifacts - export StanModel, name, model_info, @@ -29,6 +27,7 @@ export StanModel, new_rng include("model.jl") +include("download.jl") include("compile.jl") """ diff --git a/julia/src/compile.jl b/julia/src/compile.jl index 0e5e486a..b0db095a 100644 --- a/julia/src/compile.jl +++ b/julia/src/compile.jl @@ -3,35 +3,53 @@ function get_make() get(ENV, "MAKE", Sys.iswindows() ? "mingw32-make.exe" : "make") end + +function validate_stan_dir(path::AbstractString) + if !isdir(path) + error("Path does not exist!\n$path") + end + if !isfile(joinpath(path, "Makefile")) + error( + "Makefile does not exist at path! Make sure it was installed correctly.\n$path", + ) + end +end + + """ get_bridgestan_path() -> String Return the path the the BridgeStan directory. -If the environment variable `BRIDGESTAN` is set, this will be returned. Otherwise, this -function downloads an artifact containing the BridgeStan repository and returns the path to -the extracted directory. +If the environment variable `BRIDGESTAN` is set, this will be returned. +Otherwise, this function downloads a matching version of BridgeStan under +a folder called `.bridgestan` in the user's home directory. + +See `set_bridgestan_path!()` to set the path from within Julia. """ function get_bridgestan_path() path = get(ENV, "BRIDGESTAN", "") if path == "" - artifact_path = artifact"bridgestan" - path = joinpath(artifact_path, only(readdir(artifact_path))) + path = CURRENT_BRIDGESTAN + try + validate_stan_dir(path) + catch + println( + "BridgeStan not found at location specified by \$BRIDGESTAN " * + "environment variable, downloading version $pkg_version to $path", + ) + get_bridgestan_src() + num_files = length(readdir(HOME_BRIDGESTAN)) + if num_files >= 5 + @warn "Found $num_files different versions of BridgeStan in $HOME_BRIDGESTAN. " * + "Consider deleting old versions to save space." + end + println("Done!") + end end return path end -function validate_stan_dir(path::AbstractString) - if !isdir(path) - error("Path does not exist!\n$path") - end - if !isfile(joinpath(path, "Makefile")) - error( - "Makefile does not exist at path! Make sure it was installed correctly.\n$path", - ) - end -end - """ set_bridgestan_path!(path) diff --git a/julia/src/download.jl b/julia/src/download.jl new file mode 100644 index 00000000..48703be0 --- /dev/null +++ b/julia/src/download.jl @@ -0,0 +1,63 @@ +using Downloads, Tar, TOML, Inflate + +""" +Windows-friendly way to get the user's home directory. +""" +function get_home() + if Sys.iswindows() + if haskey(ENV, "USERPROFILE") + userhome = ENV["USERPROFILE"] + elseif !haskey(ENV, "HOMEPATH") + userhome = path + else + drive = get(ENV, "HOMEDRIVE", "") + userhome = joinpath(drive, ENV["HOMEPATH"]) + end + return userhome + else + return expanduser("~/") + end +end + +function get_version() + return VersionNumber( + TOML.parsefile(joinpath(dirname(@__DIR__), "Project.toml"))["version"], + ) +end +const pkg_version = get_version() + +const HOME_BRIDGESTAN = joinpath(get_home(), ".bridgestan") +const CURRENT_BRIDGESTAN = joinpath(HOME_BRIDGESTAN, "bridgestan-$pkg_version") + +const RETRIES = 5 + + +function get_bridgestan_src() + + url = + "https://github.com/roualdes/bridgestan/releases/download/" * + "v$pkg_version/bridgestan-$pkg_version.tar.gz" + mkpath(HOME_BRIDGESTAN) + tmp = nothing + err_text = "Failed to download BridgeStan $pkg_version from github.com." + for i = 1:RETRIES + try + tmp = Downloads.download(url) + break + catch + if i == RETRIES + error(err_text) + end + println(err_text) + println("Retrying ($(i+1)/$RETRIES)...") + sleep(1) + end + end + + try + tmp_extr = Tar.extract(IOBuffer(Inflate.inflate_gzip(tmp)), copy_symlinks = true) + mv(joinpath(tmp_extr, "bridgestan-$pkg_version"), CURRENT_BRIDGESTAN) + catch + error("Failed to unpack $tmp during installation") + end +end diff --git a/julia/updateArtifacts.jl b/julia/updateArtifacts.jl deleted file mode 100644 index 71dbc08f..00000000 --- a/julia/updateArtifacts.jl +++ /dev/null @@ -1,28 +0,0 @@ -import Pkg; -Pkg.add("Inflate"); -using Tar, Inflate, SHA, TOML - -filename = ARGS[1] -version = ARGS[2] - -data = Dict( - "bridgestan" => Dict( - "git-tree-sha1" => Tar.tree_hash(IOBuffer(inflate_gzip(filename))), - "lazy" => true, - "download" => [ - Dict( - "sha256" => bytes2hex(open(sha256, filename)), - "url" => string( - "https://github.com/roualdes/bridgestan/releases/download/", - version, - "/", - filename, - ), - ), - ], - ), -) - -open("julia/Artifacts.toml", "w") do io - TOML.print(io, data) -end