diff --git a/src/bundling.jl b/src/bundling.jl index 66d74b1..74cf04d 100644 --- a/src/bundling.jl +++ b/src/bundling.jl @@ -11,6 +11,22 @@ function bundle_products(recipe::BundleRecipe) return end + # Wipe top-level subdirs of a previous bundle so re-runs are idempotent + # (PackageCompiler's bundle_cert/bundle_artifacts cp without force). Only + # touch directories: top-level files are link products (the main artifact + # plus, on Windows, its sibling `.dll.a` import library) and must survive. + # Skip any subdir that contains the link output (programmatic API allows + # outname inside output_dir). + out_abs = abspath(recipe.output_dir) + outname_abs = abspath(recipe.link_recipe.outname) + if isdir(out_abs) + for entry in readdir(out_abs, join=true) + isdir(entry) || continue + startswith(relpath(outname_abs, abspath(entry)), "..") || continue + rm(entry; force=true, recursive=true) + end + end + # Ensure the bundle output directory exists mkpath(recipe.output_dir) @@ -24,22 +40,14 @@ function bundle_products(recipe::BundleRecipe) # Re-home bundled libraries into the desired bundle layout libdir = recipe.libdir - # Move `/julia` -> `//julia` + # Move `/julia` -> `//julia`. src_julia_dir = joinpath(recipe.output_dir, "julia") if isdir(src_julia_dir) dest_root = joinpath(recipe.output_dir, libdir) mkpath(dest_root) dest_julia_dir = joinpath(dest_root, "julia") if abspath(src_julia_dir) != abspath(dest_julia_dir) - if isdir(dest_julia_dir) - # Track this directory for removal in the consolidation function - dirs_to_remove = [dest_julia_dir] - else - dirs_to_remove = String[] - end mv(src_julia_dir, dest_julia_dir; force=true) - else - dirs_to_remove = String[] end # On Windows, place required DLLs next to the executable (in bin/) for loader discovery if Sys.iswindows() @@ -55,8 +63,6 @@ function bundle_products(recipe::BundleRecipe) end end end - else - dirs_to_remove = String[] end # Determine where to place the built product within the bundle @@ -83,11 +89,6 @@ function bundle_products(recipe::BundleRecipe) if Sys.isapple() _codesign_bundle!(recipe) end - - # Now perform all directory removals at once - for dir in dirs_to_remove - rm(dir; force=true, recursive=true) - end end function remove_unnecessary_libraries(recipe::BundleRecipe) diff --git a/src/linking.jl b/src/linking.jl index 5038909..67c8864 100644 --- a/src/linking.jl +++ b/src/linking.jl @@ -126,6 +126,13 @@ function link_products(recipe::LinkRecipe) try mkpath(dirname(recipe.outname)) is_shared_output = image_recipe.output_type != "--output-exe" + # Reject output paths that resolve to an existing directory; on case-insensitive + # filesystems this also catches case-only collisions with the package dir (#132). + if isdir(recipe.outname) + error("Output path '$(recipe.outname)' already exists as a directory " * + "(case-insensitive filesystems may match a differently-cased dir " * + "such as the package directory). Choose a different output name.") + end # Base command cmd2 = `$(compiler_cmd)` for f in recipe.ld_flags @@ -137,16 +144,18 @@ function link_products(recipe::LinkRecipe) end # Link in the whole archive and user-provided objects, then undo WHOLE_ARCHIVE cmd2 = `$cmd2 -Wl,$(Base.Linking.WHOLE_ARCHIVE) $(image_recipe.img_path) $(image_recipe.extra_objects) -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $(julia_libs)` - # Platform-specific linker flags + # install_name / soname / import-lib are shared-library only. lib_name = basename(recipe.outname) - if Sys.iswindows() - lib_basename, _ = splitext(lib_name) - import_lib_path = joinpath(dirname(recipe.outname), lib_basename * ".dll.a") - cmd2 = `$cmd2 -Wl,--out-implib=$(import_lib_path)` - elseif Sys.isapple() - cmd2 = `$cmd2 -Wl,-install_name,@rpath/$(lib_name)` - elseif Sys.islinux() - cmd2 = `$cmd2 -Wl,-soname,$(lib_name)` + if is_shared_output + if Sys.iswindows() + lib_basename, _ = splitext(lib_name) + import_lib_path = joinpath(dirname(recipe.outname), lib_basename * ".dll.a") + cmd2 = `$cmd2 -Wl,--out-implib=$(import_lib_path)` + elseif Sys.isapple() + cmd2 = `$cmd2 -Wl,-install_name,@rpath/$(lib_name)` + elseif Sys.islinux() + cmd2 = `$cmd2 -Wl,-soname,$(lib_name)` + end end image_recipe.verbose && println("Running: $cmd2") run(cmd2)