diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa7b104..b86f9e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,7 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-version }} + opam-disable-sandboxing: ${{ runner.os == 'macOS' && 'true' || 'false' }} windows-environment: ${{ matrix.os.windows_environment == '' && 'cygwin' || matrix.os.windows_environment }} opam-repositories: | default: git+https://github.com/ocaml/opam-repository.git @@ -44,14 +45,20 @@ jobs: - name: Build with make run: | - opam exec -- ${{ runner.os == 'Windows' && 'sh ' || '' }}./configure + opam exec -- ${{ runner.os == 'Windows' && 'sh ' || '' }}./configure -sitelib . echo ------------ grep "[\\\\/]" Makefile.config echo ------------ opam exec -- make all opt - name: Build and install with opam - run: opam pin add ocamlfind . + id: opam + env: + SOURCE_DATE_EPOCH: 0 + OCAMLPARAM: ${{ runner.os == 'macOS' && '|_|cclib=-Wl,-S' || '' }} + run: | + opam pin add ocamlfind . + sh -c 'echo "relocatable=$(test -n "$(opam list --installed --short compiler-cloning.enabled)" && echo "true" || echo "false")" >> "$GITHUB_OUTPUT"' - name: Test the ocamlfind binary env: @@ -67,3 +74,29 @@ jobs: - name: Test the toplevel plugin run: opam exec -- ocaml .github/workflows/toplist.ml + + - name: Create second switch (relocatable) + if: steps.opam.outputs.relocatable == 'true' + shell: bash + env: + SOURCE_DATE_EPOCH: 0 + OCAMLPARAM: ${{ runner.os == 'macOS' && '|_|cclib=-Wl,-S' || '' }} + run: | + opam switch create second --no-switch $(opam list --color=never --columns=package --short --installed ocaml | tr -d '\r') + opam pin add --switch=second ocamlfind . + + - name: Check ocamlfind is relocatable + if: steps.opam.outputs.relocatable == 'true' + shell: bash + run: | + first="$(opam var bin | tr -d '\r')/ocamlfind${{ runner.os == 'Windows' && '.exe' || '' }}" + second="$(opam var --switch=second bin | tr -d '\r')/ocamlfind${{ runner.os == 'Windows' && '.exe' || '' }}" + sha1sum "$first" "$second" + if ! cmp -s "$first" "$second"; then + if command -v xxd > /dev/null; then + diff <(xxd "$first") <(xxd "$second") || true + else + diff <(od -A x -t x1 -v "$first") <(od -A x -t x1 -v "$second") || true + fi + exit 1 + fi diff --git a/.github/workflows/toplist.ml b/.github/workflows/toplist.ml index b939f61..11a001c 100644 --- a/.github/workflows/toplist.ml +++ b/.github/workflows/toplist.ml @@ -1,8 +1,26 @@ #use "topfind";; #list;; +#require "unix";; #require "findlib";; +let realpath = Filename.concat (Sys.getcwd()) "src/findlib/realpath.ml" in +Topdirs.dir_use Format.err_formatter realpath;; let () = let ld_conf = Findlib.ocaml_ldconf () in - let print_line = Printf.printf " %s\n" in + let print_line {Findlib.raw = raw; Findlib.eff = eff} = + let real = realpath eff in + let real = + if real = eff then + "" + else + "\n -> " ^ real + in + let eff = + if raw ^ real = eff then + "" + else + "\n -> " ^ eff + in + Printf.printf " %s%s%s\n" raw eff real + in Printf.printf "Reading %s\n" ld_conf; List.iter print_line (Findlib.read_ldconf ld_conf);; diff --git a/.gitignore b/.gitignore index 0802cb6..899f136 100644 --- a/.gitignore +++ b/.gitignore @@ -18,12 +18,14 @@ findlib.conf ocargs.log src/findlib/depend src/findlib/findlib_config.ml +src/findlib/findlib_config_?.mlp src/findlib/fl_meta.ml src/findlib/ocaml_args.ml src/findlib/ocamlfind src/findlib/ocamlfind_opt +src/findlib/realpath.ml src/findlib/topfind src/findlib/topfind.ml -src/findlib/topfind.compat.in +src/findlib/topfind_??.p tools/extract_args/extract_args tools/extract_args/extract_args.ml diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..3c55355 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,16 @@ +include Makefile.config + +ifeq "$(IS_RELOCATABLE)" "1" + HASH := \# + SRCDIR_ENCODED = $(subst =,%+,$(subst :,%.,$(subst %,%$(HASH),$(SRCDIR_ABS)))) + SRCDIR_ABS_REAL := $(shell realpath $(SRCDIR_ABS) 2>/dev/null) + SRCDIR_REAL_ENCODED = \ + $(subst =,%+,$(subst :,%.,$(subst %,%$(HASH),$(SRCDIR_ABS_REAL)))) + BUILD_PATH_PREFIX_MAP ?= + export BUILD_PATH_PREFIX_MAP := \ + $(BUILD_PATH_PREFIX_MAP)$\ + :.=$(SRCDIR_ENCODED)$\ + $(if $(SRCDIR_REAL_ENCODED),:.=$(SRCDIR_REAL_ENCODED)) +endif + +include Makefile diff --git a/Makefile.config.pattern b/Makefile.config.pattern index e578640..6565616 100644 --- a/Makefile.config.pattern +++ b/Makefile.config.pattern @@ -3,6 +3,11 @@ # Note that there are other files containing parts of the # configuration, especially the site-lib/*/META files. # +#---------------------------------------------------------------------- +# Where is findlib being compiled: +#---------------------------------------------------------------------- +SRCDIR_ABS=/home/user/ocamlfind + #---------------------------------------------------------------------- # Where the OCAML core is installed: #---------------------------------------------------------------------- @@ -21,7 +26,13 @@ OCAML_THREADS=vm # Where the site-lib directory will be #---------------------------------------------------------------------- OCAML_SITELIB=/usr/local/lib/ocaml/site-lib -OCAML_SITELIB_NATIVE=$(OCAML_SITELIB) + +#---------------------------------------------------------------------- +# For Relocatable Findlib, the path of site-lib relative to the OCaml +# Standard Library directory. For normal Findlib, equal to +# $(OCAML_SITELIB) +#---------------------------------------------------------------------- +OCAML_SITELIB_FROM_STDLIB=$(OCAML_SITELIB) #---------------------------------------------------------------------- # Where binaries and manual pages will be installed @@ -32,8 +43,9 @@ OCAMLFIND_MAN=/usr/local/man #---------------------------------------------------------------------- # The absolute location of the configuration file #---------------------------------------------------------------------- -OCAMLFIND_CONF=/usr/local/etc/ocamlfind.conf +OCAMLFIND_CONF=/usr/local/etc/$(OCAMLFIND_CONF_NAME) OCAMLFIND_CONF_NATIVE=$(OCAMLFIND_CONF) +OCAMLFIND_CONF_NAME=ocamlfind.conf #---------------------------------------------------------------------- # Autolinking is usually on @@ -67,6 +79,12 @@ INSTALL_TOPFIND=1 #---------------------------------------------------------------------- RELATIVE_PATHS=0 +#---------------------------------------------------------------------- +# Whether Relocatable Findlib is being built +#---------------------------------------------------------------------- +IS_RELOCATABLE=0 +RELOCATABLE= + #---------------------------------------------------------------------- # Whether make install should update Makefile.packages just before # running diff --git a/configure b/configure index 9697484..cb864e9 100755 --- a/configure +++ b/configure @@ -278,45 +278,149 @@ fi ocaml_major="$(ocamlc -vnum 2>/dev/null | cut -f1 -d.)" if [ -z "$ocaml_major" ]; then ocaml_major=3; fi +# Find out the directory where ocamlc is: + +ocamlc=$(get_path ocamlc) +ocaml_core_bin=$(dirname "${ocamlc}") + +# Set the directory of ocamlfind: + +test -n "$ocamlfind_bin" || ocamlfind_bin="$ocaml_core_bin" +if [ ${use_cygpath} -gt 0 ]; then + cygpath_to_unix ocamlfind_bin +fi + +# Guess the right directory for the configuration file: + +if [ -z "${ocamlfind_config}" ]; then + d="$ocaml_core_bin" + case "$d" in + */bin) + if [ -f "$(dirname "$d")/lib/findlib.conf" ]; then + ocamlfind_config="$(dirname "$d")/lib/findlib.conf" + else + ocamlfind_config="$(dirname "$d")/etc/findlib.conf" + fi + ;; + *) + ocamlfind_config=/usr/local/etc/findlib.conf + # Fallback value + ;; + esac + if [ ${use_cygpath} -gt 0 ]; then + ocamlfind_config_native="$(cygpath -w "$ocamlfind_config")" + fi +else + ocamlfind_config_native="$ocamlfind_config" + if [ ${use_cygpath} -gt 0 ]; then + cygpath_to_unix ocamlfind_config + fi +fi + # Set site-lib directory: +relative_sitelib=0 +relocatable_findlib=0 +relocatable_ocaml_flag='' +ocamlfind_conf_from_stdlib='' if [ -z "$ocaml_sitelib" ]; then case "$ocaml_core_stdlib" in /opt/*) ocaml_sitelib=$(dirname "${ocaml_core_stdlib}")/site-lib - ocaml_sitelib_native="$(dirname "$ocaml_core_stdlib_native")${dir_sep}site-lib" + ocaml_sitelib_native="$(dirname "$ocaml_core_stdlib_native")${dir_sep}site-lib" ;; *) ocaml_sitelib="${ocaml_core_stdlib}/site-lib" - ocaml_sitelib_native="${ocaml_core_stdlib_native}${dir_sep}site-lib" + ocaml_sitelib_native="${ocaml_core_stdlib_native}${dir_sep}site-lib" ;; esac + ocaml_sitelib_from_stdlib="$ocaml_sitelib_native" + ocamlfind_conf_destdir="$ocaml_sitelib_native" else ocaml_sitelib_native="$ocaml_sitelib" if [ ${use_cygpath} -gt 0 ]; then cygpath_to_unix ocaml_sitelib fi -fi -ocamlpath="${ocaml_sitelib_native}" -if [ "$ocaml_major" -ge 5 ]; then - # OCaml 5.0+ installs its own META files under the stdlib directory. - # If findlib has been configured -sitelib $(ocamlc -where) then there's - # nothing to do, but otherwise we need to put OCaml's Standard Library - # into the path setting. - if [ ! -e "${ocaml_sitelib}/stdlib.cmi" ]; then - ocamlpath="${ocaml_core_stdlib_native}${path_sep}${ocamlpath}" + case "$ocaml_sitelib" in + .|..|./*|../*) + relative_sitelib=1 + ocaml_core_stdlib_relative="$(ocamlc -config 2>/dev/null | tr -d '\r' | sed -n -e 's/^standard_library_relative: //p')" + d1="$(cd "$ocaml_core_stdlib" 2>/dev/null && pwd)" + d2="$(dirname "$ocamlfind_config")" + if test -d "$d2"; then + d2="$(cd "$d2" 2>/dev/null && pwd)" + fi + d3="${d1#$d2}" + # Relocatable Findlib requested + # 1. Determine if the compiler is Relocatable OCaml + if [ -z "$ocaml_core_stdlib_relative" ]; then + non_relocatable_reason="the compiler does not support it" + # 2. Check that $ocamlfind_bin and $ocaml_core_bin are the same directory + elif [ "x$(cd "$ocamlfind_bin" 2>/dev/null && pwd)" != "x$(cd "$ocaml_core_bin" 2>/dev/null && pwd)" ]; then + non_relocatable_reason="ocamlfind isn't being installed with the compiler" + # 3. Check that $ocaml_core_stdlib is a directory inside $(dirname $ocamlfind_config) + elif [ "x$d1" = "x${d1#$d2}" ] || [ "x$d3" = "x{$d3##/}" ]; then + non_relocatable_reason="findlib.conf and the Standard Library don't share a common path" + else + relocatable_findlib=1 + relocatable_ocaml_flag="-set-runtime-default 'standard_library_default=$ocaml_core_stdlib_relative'" + ocaml_core_stdlib_suffix="$d3" + ocamlfind_conf_destdir="$ocaml_sitelib_native" + ocaml_sitelib="$d2" + ocaml_config_from_stdlib="$(echo "$d3" | sed -e 's|[^/]*[^/]|..|g')" + ocaml_config_from_stdlib="${ocaml_config_from_stdlib##/}" + ocaml_sitelib_from_stdlib="$ocaml_config_from_stdlib" + if [ "x$ocamlfind_conf_destdir" != 'x.' ]; then + ocaml_sitelib_from_stdlib="${ocaml_sitelib_from_stdlib}${dir_sep}${ocamlfind_conf_destdir}" + fi + # d3 should be of the form "/.../.../...". Convert all the + # characters between slashes to Filaname.dirname and then remove + # the slashes. macOS sed doesn't support EREs. + ocamlfind_conf_from_stdlib="fun p -> $(echo "$d3" | sed -e 's|[^/]*[^/]|Filename.dirname (|g;s|/||g')p$(echo "$d3" | sed -e 's|[^/]*[^/]|)|g;s|/||g')" + fi + ;; + esac + + if [ $relocatable_findlib -eq 0 ]; then + if [ $relative_sitelib -eq 1 ]; then + if [ "$ocaml_sitelib" != '.' ]; then + ocaml_sitelib="$(dirname "$ocamlfind_config")/$ocaml_sitelib" + ocaml_sitelib_from_stdlib="$(dirname ocamlfind_config_native)$dir_sep$ocaml_sitelib" + else + ocaml_sitelib="$(dirname "$ocamlfind_config")" + ocaml_sitelib_from_stdlib="$(dirname "$ocamlfind_config_native")" + fi + ocamlfind_conf_destdir="$(dirname "$ocamlfind_config_native")${dir_sep}${ocaml_sitelib_native}" + else + ocamlfind_conf_destdir="$ocaml_sitelib_native" + ocaml_sitelib_from_stdlib="$ocaml_sitelib_native" + fi fi fi -# Find out the directory where ocamlc is: - -ocamlc=$(get_path ocamlc) -ocaml_core_bin=$(dirname "${ocamlc}") - -# Set the directory of ocamlfind: - -test -n "$ocamlfind_bin" || ocamlfind_bin="$ocaml_core_bin" -if [ ${use_cygpath} -gt 0 ]; then - cygpath_to_unix ocamlfind_bin +if [ $relocatable_findlib -eq 0 ]; then + ocamlpath="$ocaml_sitelib_native" + if [ "$ocaml_major" -ge 5 ]; then + # OCaml 5.0+ installs its own META files under the stdlib directory. + # If findlib has been configured -sitelib $(ocamlc -where) then there's + # nothing to do, but otherwise we need to put OCaml's Standard Library + # into the path setting. + if [ ! -e "${ocaml_sitelib}/stdlib.cmi" ]; then + ocamlpath="${ocaml_core_stdlib_native}${path_sep}${ocamlpath}" + fi + fi +else + # $ocamlfind_conf_destdir is a native relative path; + # $ocaml_core_stdlib_suffix begins a separator; they are both Unix paths. + # For a typical (opam) setup (with `-sitelib .` passed to configure) + # $ocamlpath will therefore be './ocaml:.' + ocaml_core_stdlib_suffix=".${ocaml_core_stdlib_suffix}" + if [ $use_cygpath -gt 0 ]; then + ocaml_core_stdlib_suffix="$(echo "$ocaml_core_stdlib_suffix" | sed -e 's;/;\\;g')" + fi + ocamlpath="${ocamlfind_conf_destdir}" + if [ "$ocaml_major" -ge 5 ]; then + ocamlpath="${ocaml_core_stdlib_suffix}${path_sep}${ocamlpath}" + fi fi # Find the directory for the manual: @@ -357,36 +461,6 @@ if [ ${use_cygpath} -gt 0 ]; then cygpath_to_unix ocamlfind_man fi -# Guess the right directory for the configuration file: - -if [ -z "${ocamlfind_config}" ]; then - d="$ocaml_core_bin" - case "$d" in - */bin) - if [ -f "$(dirname "$d")/lib/findlib.conf" ]; then - ocamlfind_config="$(dirname "$d")/lib/findlib.conf" - else - ocamlfind_config="$(dirname "$d")/etc/findlib.conf" - fi - ;; - *) - ocamlfind_config=/usr/local/etc/findlib.conf - # Fallback value - ;; - esac - ocamlfind_config_native="$ocamlfind_config" - # The guessing was done using Unix notation, so convert the path back to - # native notation (use_cygpath=0 for the Cygwin port) - if [ ${use_cygpath} -gt 0 ]; then - ocamlfind_config_native="$(cygpath -w "$ocamlfind_config")" - fi -else - ocamlfind_config_native="$ocamlfind_config" - if [ ${use_cygpath} -gt 0 ]; then - cygpath_to_unix ocamlfind_config - fi -fi - ###################################################################### # do we have #remove_directory? @@ -394,6 +468,21 @@ echo "Checking for #remove_directory..." have_remdir=1 ocaml itest-aux/remdir.ml >/dev/null 2>/dev/null || have_remdir=0 +###################################################################### +# do we have Unix.realpath? + +printf "Checking for Unix.realpath... " +have_realpath=1 +ocamlc -I +unix -c itest-aux/realpath.ml >/dev/null 2>/dev/null || have_realpath=0 +rm -f itest-aux/realpath.cmi itest-aux/realpath.cmo +if [ $have_realpath -eq 1 ] && [ $relocatable_findlib -eq 1 ]; then + realpath_function='Unix.realpath' + echo "yes" +else + realpath_function='fun p -> p' + echo "no" +fi + ###################################################################### # Test the threading model @@ -764,17 +853,25 @@ else relative_paths="false" fi +srcdir_abs="$PWD" +if [ ${use_cygpath} -gt 0 ]; then + srcdir_abs="$(LC_ALL=C.UTF-8 cygpath -w -- "$srcdir_abs")" +fi + { echo "# Makefile.config written by configure" + echo "SRCDIR_ABS=${srcdir_abs}" echo "OCAML_CORE_STDLIB=${ocaml_core_stdlib}" echo "OCAML_CORE_STDLIB_NATIVE=${ocaml_core_stdlib_native}" echo "OCAML_SITELIB=${ocaml_sitelib}" - echo "OCAML_SITELIB_NATIVE=${ocaml_sitelib_native}" + echo "OCAML_SITELIB_FROM_STDLIB=${ocaml_sitelib_from_stdlib}" echo "OCAML_THREADS=${ocaml_threads}" echo "OCAMLFIND_BIN=${ocamlfind_bin}" echo "OCAMLFIND_MAN=${ocamlfind_man}" echo "OCAMLFIND_CONF=${ocamlfind_config}" echo "OCAMLFIND_CONF_NATIVE=${ocamlfind_config_native}" + echo "OCAMLFIND_CONF_NAME=$(basename "${ocamlfind_config}")" + echo "CONFIG_FROM_STDLIB=${ocamlfind_conf_from_stdlib}" echo "OCAMLFIND_OCAMLFLAGS=${ocamlfind_ocamlflags}" echo "OCAMLFIND_ARCHIVES=${ocamlfind_archives}" echo "OCAML_AUTOLINK=${ocaml_autolink}" @@ -786,11 +883,14 @@ fi echo "INSTALL_TOPFIND=${with_topfind}" echo "RELATIVE_PATHS=${relative_paths}" echo "HAVE_NATDYNLINK=${have_natdynlink}" + echo "IS_RELOCATABLE=${relocatable_findlib}" + echo "RELOCATABLE=${relocatable_ocaml_flag}" echo "VERSION=${version}" echo "ENABLE_TOPFIND_PPXOPT=${enable_topfind_ppxopt}" echo "SYSTEM=${system}" echo "NUMTOP=${numtop}" echo "SH=${sh}" + echo "REALPATH=${realpath_function}" echo "OPAQUE=${opaque}" echo "OCAMLOPT_G=${native_debugging_info}" echo "CHECK_BEFORE_INSTALL=${check_before_install}" @@ -808,7 +908,6 @@ fi fi echo "path=\"$(echo "$ocamlpath" | sed -e 's;\\;\\\\;g')\"" } > findlib.conf.in - echo "SITELIB_META=${generated_META}" >Makefile.packages # All OK @@ -833,6 +932,15 @@ else echo " topfind script: omitted" fi +if [ $relative_sitelib -gt 0 ]; then + if [ $relocatable_findlib -gt 0 ]; then + echo "Build Relocatable Findlib: yes" + else + echo "Build Relocatable Findlib: no, because $non_relocatable_reason" + fi +else + echo "Build Relocatable Findlib: no" +fi if [ $with_ppxopt -gt 0 ]; then echo "Topfind ppxopt support: yes" else diff --git a/itest-aux/realpath.ml b/itest-aux/realpath.ml new file mode 100644 index 0000000..24a05fd --- /dev/null +++ b/itest-aux/realpath.ml @@ -0,0 +1 @@ +let _ = Unix.realpath diff --git a/opam b/opam index 91ef97c..e6e2f40 100644 --- a/opam +++ b/opam @@ -19,7 +19,7 @@ build: [ [ "./configure" "-bindir" bin - "-sitelib" lib + "-sitelib" "." "-mandir" man "-config" "%{lib}%/findlib.conf" "-no-custom" diff --git a/src/findlib/Makefile b/src/findlib/Makefile index f28854d..7998b9c 100644 --- a/src/findlib/Makefile +++ b/src/findlib/Makefile @@ -30,7 +30,7 @@ TOBJECTS = topfind.cmo XOBJECTS = $(OBJECTS:.cmo=.cmx) TXOBJECTS = $(TOBJECTS:.cmo=.cmx) -OCAMLFIND_OBJECTS = ocaml_args.cmo frontend.cmo +OCAMLFIND_OBJECTS = ocaml_args.cmo realpath.cmo frontend.cmo OCAMLFIND_XOBJECTS = $(OCAMLFIND_OBJECTS:.cmo=.cmx) # OCAMLFIND_ARCHIVES: set in Makefile.config @@ -52,11 +52,13 @@ num-top: num_top.cma ocamlfind$(EXEC_SUFFIX): findlib.cma $(OCAMLFIND_OBJECTS) $(OCAMLC) $(CUSTOM) -o ocamlfind$(EXEC_SUFFIX) -g $(OCAMLFIND_ARCHIVES) \ - $(OCAMLC_FLAGS) $(OCAMLFIND_OCAMLFLAGS) $(OCAMLFIND_OBJECTS) + $(OCAMLC_FLAGS) $(OCAMLFIND_OCAMLFLAGS) $(RELOCATABLE) \ + $(OCAMLFIND_OBJECTS) ocamlfind_opt$(EXEC_SUFFIX): findlib.cmxa $(OCAMLFIND_XOBJECTS) $(OCAMLOPT) -o ocamlfind_opt$(EXEC_SUFFIX) $(OCAMLFIND_XARCHIVES) \ - $(OCAMLOPT_FLAGS) $(OCAMLFIND_OCAMLFLAGS) $(OCAMLFIND_XOBJECTS) + $(OCAMLOPT_FLAGS) $(OCAMLFIND_OCAMLFLAGS) $(RELOCATABLE) \ + $(OCAMLFIND_XOBJECTS) test_parser$(EXEC_SUFFIX): fl_metascanner.cmx test_parser.cmx fl_metatoken.cmx fl_meta.cmx $(OCAMLOPT) -o test_parser$(EXEC_SUFFIX) fl_meta.cmx fl_metatoken.cmx fl_metascanner.cmx test_parser.cmx @@ -88,15 +90,26 @@ findlib_dynload.cmxa: $(DYNLOAD_XOBJECTS) $(OCAMLOPT_SHARED) -shared -o findlib_dynload.cmxs $(DYNLOAD_XOBJECTS); \ fi -findlib_config.ml: findlib_config.mlp $(TOP)/Makefile.config - cat findlib_config.mlp | \ +findlib_config_0.mlp: findlib_config.mlp + sed -e '/^rx:/d;s/^ax://g' $< > $@ + +findlib_config_1.mlp: findlib_config.mlp + sed -e '/^ax:/d;s/^rx://g' $< > $@ + +findlib_config.ml: findlib_config_$(IS_RELOCATABLE).mlp $(TOP)/Makefile.config + cat $< | \ $(SH) $(TOP)/tools/patch '@CONFIGFILE@' '$(OCAMLFIND_CONF_NATIVE)' | \ + $(SH) $(TOP)/tools/patch '@CONFIGFILENAME@' '$(OCAMLFIND_CONF_NAME)' | \ $(SH) $(TOP)/tools/patch '@STDLIB@' '$(OCAML_CORE_STDLIB_NATIVE)' | \ $(SH) $(TOP)/tools/patch '@RELATIVE_PATHS@' '$(RELATIVE_PATHS)' | \ sed -e 's;@AUTOLINK@;$(OCAML_AUTOLINK);g' \ -e 's;@SYSTEM@;$(SYSTEM);g' \ + -e 's;@CONFIG_FROM_STDLIB@;$(CONFIG_FROM_STDLIB);g' \ >findlib_config.ml +realpath.ml: $(TOP)/Makefile.config + echo 'let realpath = $(REALPATH)' > $@ + topfind.ml: topfind.ml.in if [ "$(ENABLE_TOPFIND_PPXOPT)" = "true" ]; then \ cp topfind.ml.in topfind.ml; \ @@ -105,17 +118,18 @@ topfind.ml: topfind.ml.in > topfind.ml ; \ fi -topfind.compat.in: topfind.in - if [ "$(OCAML_REMOVE_DIRECTORY)" = "1" ]; then \ - cp $< $@; \ - else \ - sed -e '/REMOVE_DIRECTORY_BEGIN/,/REMOVE_DIRECTORY_END/ d' $< \ - > $@ ; \ - fi +topfind_00.p: topfind.in + sed -e '/^[4r]x:/d' $< > $@ + +topfind_10.p: topfind.in + sed -e 's/^4x://;/^rx:/d' $< > $@ + +topfind_11.p: topfind.in + sed -e 's/^[4r]x://' $< > $@ -topfind: topfind.compat.in - cat topfind.compat.in | \ - $(SH) $(TOP)/tools/patch '@SITELIB@' '$(OCAML_SITELIB_NATIVE)' | \ +topfind: topfind_$(OCAML_REMOVE_DIRECTORY)$(IS_RELOCATABLE).p $(TOP)/Makefile.config + cat $< | \ + $(SH) $(TOP)/tools/patch '@SITELIB@' '$(OCAML_SITELIB_FROM_STDLIB)' | \ $(SH) $(TOP)/tools/patch '@RELATIVE_PATHS@' '$(RELATIVE_PATHS)' \ >topfind @@ -124,7 +138,8 @@ num_top.cma: $(NUMTOP_OBJECTS) clean: rm -f *.cmi *.cmo *.cma *.cmx *.a *.lib *.o *.obj *.cmxa \ - fl_meta.ml findlib_config.ml topfind.ml topfind.compat.in topfind \ + fl_meta.ml findlib_config.ml findlib_config_?.mlp realpath.ml \ + topfind.ml topfind_??.p topfind \ ocamlfind$(EXEC_SUFFIX) ocamlfind_opt$(EXEC_SUFFIX) install: all diff --git a/src/findlib/findlib.ml b/src/findlib/findlib.ml index c8d72f3..b75a7ff 100644 --- a/src/findlib/findlib.ml +++ b/src/findlib/findlib.ml @@ -217,6 +217,32 @@ let init |> relocate_paths with Not_found -> default in + let convert_relative path = + let path_dirname = Filename.dirname path in + if path_dirname = Filename.current_dir_name && + Filename.basename path = Filename.current_dir_name then + (* path = "." || path = "./" *) + Filename.dirname config_file + else if path_dirname = Filename.current_dir_name && + not (Filename.is_implicit path) then + (* path = "./*" *) + let rec split acc dir = + let dirname = Filename.dirname dir in + let basename = Filename.basename dir in + if dirname = Filename.current_dir_name then + List.fold_left Filename.concat "" (basename :: acc) + else + split (Filename.basename dir :: acc) dirname in + Filename.concat (Filename.dirname config_file) (split [] path) + else if path = Filename.parent_dir_name || + Filename.is_relative path && not (Filename.is_implicit path) then + Filename.concat (Filename.dirname config_file) path + else + path in + let lookup_path name default = + let value = Fl_split.path (lookup name default) in + List.map convert_relative value in + let lookup name default = convert_relative (lookup name default) in let config_tuple = ( (lookup "ocamlc" ocamlc_default), (lookup "ocamlopt" ocamlopt_default), @@ -227,7 +253,7 @@ let init (lookup "ocamldep" ocamldep_default), (lookup "ocamlbrowser" ocamlbrowser_default), (lookup "ocamldoc" ocamldoc_default), - Fl_split.path (lookup "path" ""), + (lookup_path "path" ""), (lookup "destdir" ""), (lookup "metadir" "none"), (lookup "stdlib" Findlib_config.ocaml_stdlib), @@ -551,14 +577,28 @@ let record_package_predicates preds = let recorded_predicates() = !rec_preds +type ldconf_entry = {raw: string; eff: string} let read_ldconf filename = let lines = ref [] in + let ldconf_dir = Filename.dirname filename in let f = open_in filename in try while true do let line = input_line f in if line <> "" then - lines := line :: !lines + (* "Explicit-relative" lines should be interpreted relative to ld.conf + since OCaml 5.4. The interpretation of non-absolute lines in ld.conf + prior to OCaml 5.4 was not useful, so this behaviour is done without + a version check (which means it also works for the backports of + Relocatable OCaml, which include this change). *) + let eff = + if line = Filename.current_dir_name || + line = Filename.parent_dir_name || + Filename.is_relative line && not (Filename.is_implicit line) then + Filename.concat ldconf_dir line + else + line in + lines := {raw = line; eff = eff} :: !lines done; assert false with diff --git a/src/findlib/findlib.mli b/src/findlib/findlib.mli index 510c398..c2bc489 100644 --- a/src/findlib/findlib.mli +++ b/src/findlib/findlib.mli @@ -238,7 +238,13 @@ val list_packages' : ?prefix:string -> unit -> string list * @param prefix Limit to the packages that starts with it. Default: unlimited *) -val read_ldconf : string -> string list +(** Entries in ld.conf *) +type ldconf_entry = { + raw: string; (** Raw entry line *) + eff: string (** Evaluated entry line *) +} + +val read_ldconf : string -> ldconf_entry list (** Reads and evaluates lines from the supplied ld.conf file. *) (** Managing dynamically loaded packages *) diff --git a/src/findlib/findlib_config.mlp b/src/findlib/findlib_config.mlp index cbcc374..4a6c9a1 100644 --- a/src/findlib/findlib_config.mlp +++ b/src/findlib/findlib_config.mlp @@ -52,7 +52,20 @@ let install_dir_from_opam_switch_prefix path = | false -> None | true -> Some path -let default = "@CONFIGFILE@" +ax:let ocaml_stdlib = "@STDLIB@";; + +rx:let ocaml_stdlib = +rx: let module Defs = struct +rx: external standard_library_default : unit -> string = "%standard_library_default" +rx: external stdlib_dirs : string -> string * string option = "caml_sys_get_stdlib_dirs" +rx: end in +rx: fst (Defs.stdlib_dirs (Defs.standard_library_default ()));; + +ax:let default = "@CONFIGFILE@";; +rx:let default = +rx: let config_dir_from_stdlib = @CONFIG_FROM_STDLIB@ in +rx: Filename.concat (config_dir_from_stdlib ocaml_stdlib) "@CONFIGFILENAME@";; + let fallback = Option.value ~default let rec try_vars = function @@ -94,8 +107,6 @@ let ocaml_has_meta_files = String.sub Sys.ocaml_version 0 (String.index Sys.ocaml_version '.') in int_of_string ocaml_major >= 5;; -let ocaml_stdlib = "@STDLIB@";; - let ocaml_ldconf = ocaml_stdlib / "ld.conf";; let ocaml_has_autolinking = @AUTOLINK@;; diff --git a/src/findlib/frontend.ml b/src/findlib/frontend.ml index bd07aca..82b95c8 100644 --- a/src/findlib/frontend.ml +++ b/src/findlib/frontend.ml @@ -231,7 +231,7 @@ let write_ldconf filename lines new_lines = try List.iter (fun line -> output_string f (line ^ "\n")) - (lines @ new_lines); + (List.map (fun {raw = line; eff = _} -> line) lines @ new_lines); close_out f; prerr_endline("Updated " ^ filename); with @@ -286,7 +286,7 @@ let conflict_report incpath pkglist = let dll_pairs = List.flatten (List.map - (fun dll_dir -> + (fun {eff = dll_dir; raw = _} -> let files = try Array.to_list (Sys.readdir dll_dir) with _ -> @@ -2206,11 +2206,14 @@ let install_package () = filesystems. So some better check would be nice. *) let lines = read_ldconf !ldconf in - let dlldir_norm = Fl_split.norm_dir dlldir in + let dlldir_real = Realpath.realpath dlldir in + let dlldir_norm = Fl_split.norm_dir dlldir_real in let dlldir_norm_lc = string_lowercase_ascii dlldir_norm in let ci_filesys = (Sys.os_type = "Win32") in let check_dir d = - let d' = Fl_split.norm_dir d in + let d_real = try Realpath.realpath d.eff with Unix.Unix_error _ -> "" in + let d' = Fl_split.norm_dir d.eff in + (d_real = dlldir_real) || (d' = dlldir_norm) || (ci_filesys && string_lowercase_ascii d' = dlldir_norm_lc) in if not (List.exists check_dir lines) then @@ -2378,9 +2381,9 @@ let remove_package () = begin let lines = read_ldconf !ldconf in let d = Fl_split.norm_dir pkgdir in - let exists = List.exists (fun p -> Fl_split.norm_dir p = d) lines in + let exists = List.exists (fun p -> Fl_split.norm_dir p.eff = d) lines in if exists then begin - let lines' = List.filter (fun p -> Fl_split.norm_dir p <> d) lines in + let lines' = List.filter (fun p -> Fl_split.norm_dir p.eff <> d) lines in write_ldconf !ldconf lines' [] end end diff --git a/src/findlib/topfind.in b/src/findlib/topfind.in index 9e04f00..3c115f6 100644 --- a/src/findlib/topfind.in +++ b/src/findlib/topfind.in @@ -3,9 +3,9 @@ (* For Ocaml-3.03 and up, so you can do: #use "topfind" and get a * working findlib toploop. *) - -(* To access Toploop in OCaml >= 4.00. This directory will be later removed from path if possible *) -#directory "+compiler-libs";; +4x: +4x:(* To access Toploop in OCaml >= 4.00. This directory will be later removed from path if possible *) +4x:#directory "+compiler-libs";; let exists path = match Sys.file_exists path with @@ -89,6 +89,14 @@ let rec try_vars = function | Some _ as found -> found)) in +let sitelib_directory = "@SITELIB@" in +rx:let module Defs = struct +rx: external standard_library_default : unit -> string = "%standard_library_default" +rx: external stdlib_dirs : string -> string * string option = "caml_sys_get_stdlib_dirs" +rx:end in +rx:let stdlib, _ = Defs.stdlib_dirs (Defs.standard_library_default ()) in +rx:let sitelib_directory = Filename.concat stdlib sitelib_directory in + let location = match try_vars [ ("OPAM_SWITCH_PREFIX", install_dir_from_opam_switch_prefix); @@ -96,12 +104,12 @@ let location = ("OCAML_TOPLEVEL_PATH", install_dir_from_ocaml_toplevel_path); ] with | Some location -> location - | None -> "@SITELIB@" + | None -> sitelib_directory in let findlib_directory = match @RELATIVE_PATHS@ with | true -> location / "lib" / "findlib" - | false -> "@SITELIB@" / "findlib" + | false -> sitelib_directory / "findlib" in let () = Topdirs.dir_directory findlib_directory in @@ -131,11 +139,9 @@ if not (exec_test "Topfind.reset;;") then ( Topdirs.dir_load Format.err_formatter (findlib_directory / ("findlib_top." ^ suffix))) ;; (* phrase has to end here otherwise Topfind is not accessible *) - -(* REMOVE_DIRECTORY_BEGIN *) -(* OCaml < 4.00 do not have this so only include this code if it was detected *) -let () = Topdirs.dir_remove_directory "+compiler-libs" in -(* REMOVE_DIRECTORY_END *) +4x: +4x:(* OCaml < 4.00 do not have this so only include this code if it was detected *) +4x:let () = Topdirs.dir_remove_directory "+compiler-libs" in (* The following is always executed. It is harmless if findlib was already * initialized