From b8df114427d5b0faccca6af9f5974197a4f1567d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 14 Jul 2021 18:54:48 +0200 Subject: [PATCH 1/9] Add Crystal::CrystalPath.default_paths returning Array(String) --- .../crystal_path/crystal_path_spec.cr | 4 +-- src/compiler/crystal/crystal_path.cr | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/spec/compiler/crystal_path/crystal_path_spec.cr b/spec/compiler/crystal_path/crystal_path_spec.cr index 8e5960dc54e2..ff11065ef17c 100644 --- a/spec/compiler/crystal_path/crystal_path_spec.cr +++ b/spec/compiler/crystal_path/crystal_path_spec.cr @@ -4,7 +4,7 @@ require "spec/helpers/iterate" private def assert_finds(search, results, relative_to = nil, path = __DIR__, file = __FILE__, line = __LINE__) it "finds #{search.inspect}", file, line do - crystal_path = Crystal::CrystalPath.new(path) + crystal_path = Crystal::CrystalPath.new([path]) results = results.map { |result| ::Path[__DIR__, result].normalize.to_s } Dir.cd(__DIR__) do matches = crystal_path.find search, relative_to: relative_to @@ -15,7 +15,7 @@ end private def assert_doesnt_find(search, relative_to = nil, path = __DIR__, expected_relative_to = nil, file = __FILE__, line = __LINE__) it "doesn't finds #{search.inspect}", file, line do - crystal_path = Crystal::CrystalPath.new(path) + crystal_path = Crystal::CrystalPath.new([path]) Dir.cd(__DIR__) do error = expect_raises Crystal::CrystalPath::NotFoundError do crystal_path.find search, relative_to: relative_to diff --git a/src/compiler/crystal/crystal_path.cr b/src/compiler/crystal/crystal_path.cr index 5cb2ffae8fd7..9ace2f9b40d5 100644 --- a/src/compiler/crystal/crystal_path.cr +++ b/src/compiler/crystal/crystal_path.cr @@ -13,22 +13,28 @@ module Crystal private DEFAULT_LIB_PATH = "lib" - def self.default_path - ENV["CRYSTAL_PATH"]? || begin - if Crystal::Config.path.blank? - DEFAULT_LIB_PATH - elsif Crystal::Config.path.split(Process::PATH_DELIMITER).includes?(DEFAULT_LIB_PATH) - Crystal::Config.path - else - {DEFAULT_LIB_PATH, Crystal::Config.path}.join(Process::PATH_DELIMITER) + def self.default_paths : Array(String) + if path = ENV["CRYSTAL_PATH"]? + path_array = path.split(Process::PATH_DELIMITER, remove_empty: true) + elsif path = Crystal::Config.path.presence + path_array = path.split(Process::PATH_DELIMITER, remove_empty: true) + unless path_array.includes?(DEFAULT_LIB_PATH) + path_array.unshift DEFAULT_LIB_PATH end + else + path_array = [DEFAULT_LIB_PATH] end + + path_array + end + + def self.default_path : String + default_paths.join(Process::PATH_DELIMITER) end property entries : Array(String) - def initialize(path = CrystalPath.default_path, codegen_target = Config.host_target) - @entries = path.split(Process::PATH_DELIMITER).reject &.empty? + def initialize(@entries : Array(String) = CrystalPath.default_paths, codegen_target = Config.host_target) add_target_path(codegen_target) end From f16873bac2aa03548b93ef3378e5f58c16e3b5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 14 Jul 2021 18:55:17 +0200 Subject: [PATCH 2/9] Add CrystalLibraryPath.default_paths returning Array(String) --- src/compiler/crystal/codegen/link.cr | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/compiler/crystal/codegen/link.cr b/src/compiler/crystal/codegen/link.cr index 86f021a4247b..32eb92df4fbb 100644 --- a/src/compiler/crystal/codegen/link.cr +++ b/src/compiler/crystal/codegen/link.cr @@ -81,12 +81,16 @@ module Crystal end class CrystalLibraryPath + def self.default_paths : Array(String) + ENV.fetch("CRYSTAL_LIBRARY_PATH", Crystal::Config.library_path).split(Process::PATH_DELIMITER, remove_empty: true) + end + def self.default_path : String - ENV.fetch("CRYSTAL_LIBRARY_PATH", Crystal::Config.library_path) + default_paths.join(Process::PATH_DELIMITER) end class_getter paths : Array(String) do - default_path.split(Process::PATH_DELIMITER, remove_empty: true) + default_paths end end From d3eb4f15b44e6fca7a64c7a7a9219ba339b233e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 27 Jul 2021 16:44:00 +0200 Subject: [PATCH 3/9] Add `CrystalPath.expand_paths` --- .../crystal_path/crystal_path_spec.cr | 20 ++++++++++++ src/compiler/crystal/crystal_path.cr | 31 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/spec/compiler/crystal_path/crystal_path_spec.cr b/spec/compiler/crystal_path/crystal_path_spec.cr index ff11065ef17c..058f2ecd156c 100644 --- a/spec/compiler/crystal_path/crystal_path_spec.cr +++ b/spec/compiler/crystal_path/crystal_path_spec.cr @@ -188,4 +188,24 @@ describe Crystal::CrystalPath do crystal_path.entries.should eq(%w(foo bar)) end end + + it ".expand_paths" do + paths = ["$ORIGIN/../foo"] + Crystal::CrystalPath.expand_paths(paths, "/usr/bin/") + paths.should eq ["/usr/bin/../foo"] + paths = ["./$ORIGIN/../foo"] + Crystal::CrystalPath.expand_paths(paths, "/usr/bin/") + paths.should eq ["./$ORIGIN/../foo"] + paths = ["$ORIGINfoo"] + Crystal::CrystalPath.expand_paths(paths, "/usr/bin/") + paths.should eq ["$ORIGINfoo"] + paths = ["lib", "$ORIGIN/../foo"] + Crystal::CrystalPath.expand_paths(paths, "/usr/bin/") + paths.should eq ["lib", "/usr/bin/../foo"] + + paths = ["$ORIGIN/../foo"] + expect_raises(Exception, "Missing executable path to expand $ORIGIN path") do + Crystal::CrystalPath.expand_paths(paths, nil) + end + end end diff --git a/src/compiler/crystal/crystal_path.cr b/src/compiler/crystal/crystal_path.cr index 9ace2f9b40d5..5eb3b8d47467 100644 --- a/src/compiler/crystal/crystal_path.cr +++ b/src/compiler/crystal/crystal_path.cr @@ -32,6 +32,37 @@ module Crystal default_paths.join(Process::PATH_DELIMITER) end + # Expand `$ORIGIN` in the paths to the directory where the compiler binary + # is located (at runtime). + # For install locations like + # `/path/prefix/bin/crystal` for the compiler + # `/path/prefix/share/crystal/src` for the standard library + # the path `$ORIGIN/../share/crystal/src` resolves to + # the standard library location. + # This generic path can be passed into the compiler via CRYSTAL_CONFIG_PATH + # to produce a portable binary that resolves the standard library path + # relative to the compiler location, independent of the absolute path. + def self.expand_paths(paths, origin) + paths.map! do |path| + if (chopped = path.lchop?("$ORIGIN")) && chopped[0].in?(::Path::SEPARATORS) + if origin.nil? + raise "Missing executable path to expand $ORIGIN path" + end + File.join(origin, chopped) + else + path + end + end + end + + def self.expand_paths(paths) + origin = nil + if executable_path = Process.executable_path + origin = File.dirname(executable_path) + end + expand_paths(paths, origin) + end + property entries : Array(String) def initialize(@entries : Array(String) = CrystalPath.default_paths, codegen_target = Config.host_target) From 577599c1c5a28cfcae0bbd87390e5be8e976e0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 27 Jul 2021 16:44:27 +0200 Subject: [PATCH 4/9] Add path expansion to CRYSTAL_PATH --- Makefile | 2 ++ man/crystal.1 | 4 +++- src/compiler/crystal/crystal_path.cr | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8e465e846327..1955f435ef20 100644 --- a/Makefile +++ b/Makefile @@ -33,10 +33,12 @@ SPEC_WARNINGS_OFF := --exclude-warnings spec/std --exclude-warnings spec/compile SPEC_FLAGS := $(if $(verbose),-v )$(if $(junit_output),--junit_output $(junit_output) ) CRYSTAL_CONFIG_LIBRARY_PATH := $(shell bin/crystal env CRYSTAL_LIBRARY_PATH 2> /dev/null) CRYSTAL_CONFIG_BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null) +CRYSTAL_CONFIG_PATH := "$$ORIGIN/../share/crystal/src" SOURCE_DATE_EPOCH := $(shell (git show -s --format=%ct HEAD || stat -c "%Y" Makefile || stat -f "%m" Makefile) 2> /dev/null) EXPORTS := \ CRYSTAL_CONFIG_LIBRARY_PATH="$(CRYSTAL_CONFIG_LIBRARY_PATH)" \ CRYSTAL_CONFIG_BUILD_COMMIT="$(CRYSTAL_CONFIG_BUILD_COMMIT)" \ + CRYSTAL_CONFIG_PATH="$(CRYSTAL_CONFIG_PATH)" \ SOURCE_DATE_EPOCH="$(SOURCE_DATE_EPOCH)" SHELL = sh LLVM_CONFIG := $(shell src/llvm/ext/find-llvm-config) diff --git a/man/crystal.1 b/man/crystal.1 index 2c54fee9f60c..614c6144903d 100644 --- a/man/crystal.1 +++ b/man/crystal.1 @@ -385,7 +385,9 @@ Defines path where Crystal caches partial compilation results for faster subsequ .Pp .It .It Sy CRYSTAL_PATH -Defines paths where Crystal searches for required files. +Defines paths where Crystal searches for required source files. Multiple paths can be separated by ":". +.Pp +The pattern '$ORIGIN' at the start of the path expands to the directory where the compiler binary is located. For example, '$ORIGIN/../share/crystal/src' resolves the standard library path relative to the compiler location in a generic way, independent of the absolute paths (assuming the relative location is correct). .Pp .It .It Sy CRYSTAL_OPTS diff --git a/src/compiler/crystal/crystal_path.cr b/src/compiler/crystal/crystal_path.cr index 5eb3b8d47467..e79beabb8ea5 100644 --- a/src/compiler/crystal/crystal_path.cr +++ b/src/compiler/crystal/crystal_path.cr @@ -25,6 +25,8 @@ module Crystal path_array = [DEFAULT_LIB_PATH] end + expand_paths(path_array) + path_array end From b427da859850ea284ac8dbc0ef12117e63cf775c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 27 Jul 2021 16:44:56 +0200 Subject: [PATCH 5/9] Add path expansion to CRYSTAL_LIBRARY_PATH --- Makefile | 2 +- man/crystal.1 | 12 ++++++++++++ src/compiler/crystal/codegen/link.cr | 6 +++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1955f435ef20..a8f576e15a1a 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ SPEC_SOURCES := $(shell find spec -name '*.cr') override FLAGS += $(if $(release),--release )$(if $(stats),--stats )$(if $(progress),--progress )$(if $(threads),--threads $(threads) )$(if $(debug),-d )$(if $(static),--static )$(if $(LDFLAGS),--link-flags="$(LDFLAGS)" )$(if $(target),--cross-compile --target $(target) ) SPEC_WARNINGS_OFF := --exclude-warnings spec/std --exclude-warnings spec/compiler SPEC_FLAGS := $(if $(verbose),-v )$(if $(junit_output),--junit_output $(junit_output) ) -CRYSTAL_CONFIG_LIBRARY_PATH := $(shell bin/crystal env CRYSTAL_LIBRARY_PATH 2> /dev/null) +CRYSTAL_CONFIG_LIBRARY_PATH := "$$ORIGIN/../lib/crystal" CRYSTAL_CONFIG_BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null) CRYSTAL_CONFIG_PATH := "$$ORIGIN/../share/crystal/src" SOURCE_DATE_EPOCH := $(shell (git show -s --format=%ct HEAD || stat -c "%Y" Makefile || stat -f "%m" Makefile) 2> /dev/null) diff --git a/man/crystal.1 b/man/crystal.1 index 614c6144903d..541163934fc2 100644 --- a/man/crystal.1 +++ b/man/crystal.1 @@ -234,6 +234,11 @@ Please see .Sm "ENVIRONMENT VARIABLES". .Pp .It +.It Sy CRYSTAL_LIBRARY_PATH +Please see +.Sm "ENVIRONMENT VARIABLES". +.Pp +.It .It Sy CRYSTAL_PATH Please see .Sm "ENVIRONMENT VARIABLES". @@ -384,6 +389,13 @@ Show version. Defines path where Crystal caches partial compilation results for faster subsequent builds. This path is also used to temporarily store executables when Crystal programs are run with 'crystal run' rather than 'crystal build'. .Pp .It +.It Sy CRYSTAL_LIBRARY_PATH +Defines paths where Crystal searches for (binary) libraries. Multiple paths can be separated by ":". +These paths are passed to the linker as `-L` flags. +.Pp +The pattern '$ORIGIN' at the start of the path expands to the directory where the compiler binary is located. For example, '$ORIGIN/../lib/crystal' resolves the standard library path relative to the compiler location in a generic way, independent of the absolute paths (assuming the relative location is correct). +.Pp +.It .It Sy CRYSTAL_PATH Defines paths where Crystal searches for required source files. Multiple paths can be separated by ":". .Pp diff --git a/src/compiler/crystal/codegen/link.cr b/src/compiler/crystal/codegen/link.cr index 32eb92df4fbb..13994aa32905 100644 --- a/src/compiler/crystal/codegen/link.cr +++ b/src/compiler/crystal/codegen/link.cr @@ -82,7 +82,11 @@ module Crystal class CrystalLibraryPath def self.default_paths : Array(String) - ENV.fetch("CRYSTAL_LIBRARY_PATH", Crystal::Config.library_path).split(Process::PATH_DELIMITER, remove_empty: true) + paths = ENV.fetch("CRYSTAL_LIBRARY_PATH", Crystal::Config.library_path).split(Process::PATH_DELIMITER, remove_empty: true) + + CrystalPath.expand_paths(paths) + + paths end def self.default_path : String From 3b28880be1135e60812918a3bc0581bb0d679006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Sun, 1 Aug 2021 20:45:35 +0200 Subject: [PATCH 6/9] Fix escaping in Makefile --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a8f576e15a1a..8910f18076b1 100644 --- a/Makefile +++ b/Makefile @@ -31,14 +31,14 @@ SPEC_SOURCES := $(shell find spec -name '*.cr') override FLAGS += $(if $(release),--release )$(if $(stats),--stats )$(if $(progress),--progress )$(if $(threads),--threads $(threads) )$(if $(debug),-d )$(if $(static),--static )$(if $(LDFLAGS),--link-flags="$(LDFLAGS)" )$(if $(target),--cross-compile --target $(target) ) SPEC_WARNINGS_OFF := --exclude-warnings spec/std --exclude-warnings spec/compiler SPEC_FLAGS := $(if $(verbose),-v )$(if $(junit_output),--junit_output $(junit_output) ) -CRYSTAL_CONFIG_LIBRARY_PATH := "$$ORIGIN/../lib/crystal" +CRYSTAL_CONFIG_LIBRARY_PATH := '$$ORIGIN/../lib/crystal' CRYSTAL_CONFIG_BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null) -CRYSTAL_CONFIG_PATH := "$$ORIGIN/../share/crystal/src" +CRYSTAL_CONFIG_PATH := '$$ORIGIN/../share/crystal/src' SOURCE_DATE_EPOCH := $(shell (git show -s --format=%ct HEAD || stat -c "%Y" Makefile || stat -f "%m" Makefile) 2> /dev/null) EXPORTS := \ - CRYSTAL_CONFIG_LIBRARY_PATH="$(CRYSTAL_CONFIG_LIBRARY_PATH)" \ + CRYSTAL_CONFIG_LIBRARY_PATH=$(CRYSTAL_CONFIG_LIBRARY_PATH) \ CRYSTAL_CONFIG_BUILD_COMMIT="$(CRYSTAL_CONFIG_BUILD_COMMIT)" \ - CRYSTAL_CONFIG_PATH="$(CRYSTAL_CONFIG_PATH)" \ + CRYSTAL_CONFIG_PATH=$(CRYSTAL_CONFIG_PATH) \ SOURCE_DATE_EPOCH="$(SOURCE_DATE_EPOCH)" SHELL = sh LLVM_CONFIG := $(shell src/llvm/ext/find-llvm-config) From 662af7014f7eb2d26ff39b642b5a0ca1b929e1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 2 Aug 2021 21:54:02 +0200 Subject: [PATCH 7/9] Fix wrapper script to set CRYSTAL_LIBRARY_PATH for local compiler --- bin/crystal | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bin/crystal b/bin/crystal index ec70846e497d..ccf53122a052 100755 --- a/bin/crystal +++ b/bin/crystal @@ -147,11 +147,13 @@ export CRYSTAL_HAS_WRAPPER=true export CRYSTAL="${CRYSTAL:-"crystal"}" -if [ -z "$CRYSTAL_CONFIG_LIBRARY_PATH" ]; then - export CRYSTAL_CONFIG_LIBRARY_PATH="$( - export PATH="$(remove_path_item "$(remove_path_item "$PATH" "$SCRIPT_ROOT")" "bin")" - crystal env CRYSTAL_LIBRARY_PATH || echo "" - )" +if [ -z "$CRYSTAL_CONFIG_LIBRARY_PATH" ] || [ -z "$CRYSTAL_LIBRARY_PATH" ]; then + CRYSTAL_INSTALLED_LIBRARY_PATH="$( + export PATH="$(remove_path_item "$(remove_path_item "$PATH" "$SCRIPT_ROOT")" "bin")" + crystal env CRYSTAL_LIBRARY_PATH || echo "" + )" + export CRYSTAL_LIBRARY_PATH=${CRYSTAL_LIBRARY_PATH:-CRYSTAL_INSTALLED_LIBRARY_PATH} + export CRYSTAL_CONFIG_LIBRARY_PATH=${CRYSTAL_CONFIG_LIBRARY_PATH:-CRYSTAL_INSTALLED_LIBRARY_PATH} fi if [ -x "$CRYSTAL_DIR/crystal" ]; then From c0af0d821996d16bf1e1ae221af0ef16b080b348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 2 Aug 2021 22:14:59 +0200 Subject: [PATCH 8/9] fixup! Fix wrapper script to set CRYSTAL_LIBRARY_PATH for local compiler --- bin/crystal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/crystal b/bin/crystal index ccf53122a052..db27e2a050a8 100755 --- a/bin/crystal +++ b/bin/crystal @@ -152,8 +152,8 @@ if [ -z "$CRYSTAL_CONFIG_LIBRARY_PATH" ] || [ -z "$CRYSTAL_LIBRARY_PATH" ]; then export PATH="$(remove_path_item "$(remove_path_item "$PATH" "$SCRIPT_ROOT")" "bin")" crystal env CRYSTAL_LIBRARY_PATH || echo "" )" - export CRYSTAL_LIBRARY_PATH=${CRYSTAL_LIBRARY_PATH:-CRYSTAL_INSTALLED_LIBRARY_PATH} - export CRYSTAL_CONFIG_LIBRARY_PATH=${CRYSTAL_CONFIG_LIBRARY_PATH:-CRYSTAL_INSTALLED_LIBRARY_PATH} + export CRYSTAL_LIBRARY_PATH=${CRYSTAL_LIBRARY_PATH:-$CRYSTAL_INSTALLED_LIBRARY_PATH} + export CRYSTAL_CONFIG_LIBRARY_PATH=${CRYSTAL_CONFIG_LIBRARY_PATH:-$CRYSTAL_INSTALLED_LIBRARY_PATH} fi if [ -x "$CRYSTAL_DIR/crystal" ]; then From 9692b9c407b1fd60770732a92947a2adb133d5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 4 Aug 2021 11:26:26 +0200 Subject: [PATCH 9/9] [Makefile] set CRYSTAL_CONFIG_LIBRARY_PATH only on compiler build --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8910f18076b1..ac3a8b670ae3 100644 --- a/Makefile +++ b/Makefile @@ -36,10 +36,11 @@ CRYSTAL_CONFIG_BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null) CRYSTAL_CONFIG_PATH := '$$ORIGIN/../share/crystal/src' SOURCE_DATE_EPOCH := $(shell (git show -s --format=%ct HEAD || stat -c "%Y" Makefile || stat -f "%m" Makefile) 2> /dev/null) EXPORTS := \ - CRYSTAL_CONFIG_LIBRARY_PATH=$(CRYSTAL_CONFIG_LIBRARY_PATH) \ CRYSTAL_CONFIG_BUILD_COMMIT="$(CRYSTAL_CONFIG_BUILD_COMMIT)" \ CRYSTAL_CONFIG_PATH=$(CRYSTAL_CONFIG_PATH) \ SOURCE_DATE_EPOCH="$(SOURCE_DATE_EPOCH)" +EXPORTS_BUILD := \ + CRYSTAL_CONFIG_LIBRARY_PATH=$(CRYSTAL_CONFIG_LIBRARY_PATH) SHELL = sh LLVM_CONFIG := $(shell src/llvm/ext/find-llvm-config) LLVM_EXT_DIR = src/llvm/ext @@ -118,7 +119,7 @@ $(O)/compiler_spec: $(DEPS) $(SOURCES) $(SPEC_SOURCES) $(O)/crystal: $(DEPS) $(SOURCES) @mkdir -p $(O) - $(EXPORTS) ./bin/crystal build $(FLAGS) -o $@ src/compiler/crystal.cr -D without_openssl -D without_zlib + $(EXPORTS) $(EXPORTS_BUILD) ./bin/crystal build $(FLAGS) -o $@ src/compiler/crystal.cr -D without_openssl -D without_zlib $(LLVM_EXT_OBJ): $(LLVM_EXT_DIR)/llvm_ext.cc $(CXX) -c $(CXXFLAGS) -o $@ $< $(shell $(LLVM_CONFIG) --cxxflags)