diff --git a/.depend b/.depend index 3cd973067390..313b430cc076 100644 --- a/.depend +++ b/.depend @@ -2336,6 +2336,7 @@ bytecomp/bytelink.cmo : \ utils/consistbl.cmi \ utils/config.cmi \ utils/compression.cmi \ + driver/compenv.cmi \ file_formats/cmo_format.cmi \ utils/clflags.cmi \ utils/ccomp.cmi \ @@ -2355,6 +2356,7 @@ bytecomp/bytelink.cmx : \ utils/consistbl.cmx \ utils/config.cmx \ utils/compression.cmx \ + driver/compenv.cmx \ file_formats/cmo_format.cmi \ utils/clflags.cmx \ utils/ccomp.cmx \ @@ -2750,6 +2752,7 @@ asmcomp/asmlink.cmo : \ utils/consistbl.cmi \ utils/config.cmi \ middle_end/compilenv.cmi \ + driver/compenv.cmi \ file_formats/cmx_format.cmi \ asmcomp/cmm_helpers.cmi \ asmcomp/cmm.cmi \ @@ -2771,6 +2774,7 @@ asmcomp/asmlink.cmx : \ utils/consistbl.cmx \ utils/config.cmx \ middle_end/compilenv.cmx \ + driver/compenv.cmx \ file_formats/cmx_format.cmi \ asmcomp/cmm_helpers.cmx \ asmcomp/cmm.cmx \ diff --git a/Changes b/Changes index d06707658c06..d273f59ce053 100644 --- a/Changes +++ b/Changes @@ -161,6 +161,12 @@ ___________ runtime assertions. (Antonin Décimo, review by Miod Vallat, Gabriel Scherer, and David Allsopp) +- #13???: Added --enable-relative which allows the runtime and the compilers to + locate the Standard Library relative to where the binaries themselves, + removing the absolute path previously embedded in + caml_standard_library_default. + (David Allsopp, review by ???) + ### Code generation and optimizations: - #13014: Enable compile-time option -function-sections on all previously diff --git a/Makefile b/Makefile index 2321aed92f8c..14eeac8d6271 100644 --- a/Makefile +++ b/Makefile @@ -1429,15 +1429,22 @@ C_LITERAL = $(shell $(SAK) encode-C-literal '$(1)') runtime/build_config.h: $(ROOTDIR)/Makefile.config $(SAK) $(V_GEN)echo '/* This file is generated from $(ROOTDIR)/Makefile.config */' > $@ && \ - echo '#define OCAML_STDLIB_DIR $(call C_LITERAL,$(LIBDIR))' >> $@ && \ - echo '#define HOST "$(HOST)"' >> $@ + echo '#define OCAML_STDLIB_DIR $(call C_LITERAL,$(LIBDIR))' >> $@ +ifneq "$(LIBDIR_REL)" "" + @echo '#define OCAML_STDLIB_DIR_REL $(call C_LITERAL,$(LIBDIR_REL))' >> $@ +endif + @echo '#define HOST "$(HOST)"' >> $@ + +runtime/stdlib.$(O): runtime/build_config.h ## Runtime libraries and programs -runtime/ocamlrun$(EXE): runtime/prims.$(O) runtime/libcamlrun.$(A) +runtime/ocamlrun$(EXE): runtime/prims.$(O) runtime/stdlib.$(O) \ + runtime/libcamlrun.$(A) $(V_MKEXE)$(MKEXE) -o $@ $^ $(BYTECCLIBS) -runtime/ocamlruns$(EXE): runtime/prims.$(O) runtime/libcamlrun_non_shared.$(A) +runtime/ocamlruns$(EXE): runtime/prims.$(O) runtime/stdlib.$(O) \ + runtime/libcamlrun_non_shared.$(A) $(V_MKEXE)$(call MKEXE_VIA_CC,$@,$^ $(BYTECCLIBS)) runtime/libcamlrun.$(A): $(libcamlrun_OBJECTS) @@ -1446,13 +1453,15 @@ runtime/libcamlrun.$(A): $(libcamlrun_OBJECTS) runtime/libcamlrun_non_shared.$(A): $(libcamlrun_non_shared_OBJECTS) $(V_MKLIB)$(call MKLIB,$@, $^) -runtime/ocamlrund$(EXE): runtime/prims.$(O) runtime/libcamlrund.$(A) +runtime/ocamlrund$(EXE): runtime/prims.$(O) runtime/stdlib.$(O) \ + runtime/libcamlrund.$(A) $(V_MKEXE)$(MKEXE) $(MKEXEDEBUGFLAG) -o $@ $^ $(BYTECCLIBS) runtime/libcamlrund.$(A): $(libcamlrund_OBJECTS) $(V_MKLIB)$(call MKLIB,$@, $^) -runtime/ocamlruni$(EXE): runtime/prims.$(O) runtime/libcamlruni.$(A) +runtime/ocamlruni$(EXE): runtime/prims.$(O) runtime/stdlib.$(O) \ + runtime/libcamlruni.$(A) $(V_MKEXE)$(MKEXE) -o $@ $^ $(INSTRUMENTED_RUNTIME_LIBS) $(BYTECCLIBS) runtime/libcamlruni.$(A): $(libcamlruni_OBJECTS) diff --git a/Makefile.build_config.in b/Makefile.build_config.in index ae649dcebaa2..32db24485244 100644 --- a/Makefile.build_config.in +++ b/Makefile.build_config.in @@ -183,3 +183,5 @@ TSAN=@tsan@ # Contains TSan-specific runtime files, or nothing if TSan support is # disabled TSAN_NATIVE_RUNTIME_C_SOURCES = @tsan_native_runtime_c_sources@ + +RELOCATABLE = @relocatable@ diff --git a/Makefile.common b/Makefile.common index 1e6a7eff8903..cc659ef824ee 100644 --- a/Makefile.common +++ b/Makefile.common @@ -156,6 +156,15 @@ ifeq "$(FUNCTION_SECTIONS)" "true" OPTCOMPFLAGS += -function-sections endif +ifeq "$(LIBDIR_REL)" "" + SET_RELATIVE_STDLIB = +else + SET_RELATIVE_STDLIB = \ +-set-global-string 'caml_standard_library_default=$(LIBDIR_REL)' +endif + +DOTOPT_LINKFLAGS = $(SET_RELATIVE_STDLIB) + # The rule to compile C files # This rule is similar to GNU make's implicit rule, except that it is more @@ -360,6 +369,7 @@ $(basename $(notdir $(1)))_NATIVE_LINKFLAGS = $(basename $(notdir $(1)))_NATIVE_LINKCMD = \ $(strip \ $$(CAMLOPT) $$(OC_COMMON_LINKFLAGS) $$(OC_NATIVE_LINKFLAGS) \ + $(DOTOPT_LINKFLAGS) \ $$($(basename $(notdir $(1)))_COMMON_LINKFLAGS) \ $$($(basename $(notdir $(1)))_NATIVE_LINKFLAGS)) diff --git a/asmcomp/asmlink.ml b/asmcomp/asmlink.ml index b2b74e3568a3..ee98dc304c25 100644 --- a/asmcomp/asmlink.ml +++ b/asmcomp/asmlink.ml @@ -335,6 +335,7 @@ let link ~ppf_dump objfiles output_name = let obj_infos = List.map read_file objfiles in let ldeps = Linkdeps.create ~complete:true in let units_tolink = List.fold_right (scan_file ldeps) obj_infos [] in + Compenv.set_caml_standard_library_default (); (match Linkdeps.check ldeps with | None -> () | Some e -> raise (Error (Link_error e))); diff --git a/bytecomp/bytelink.ml b/bytecomp/bytelink.ml index 14a680860c28..2b4f119db580 100644 --- a/bytecomp/bytelink.ml +++ b/bytecomp/bytelink.ml @@ -780,6 +780,8 @@ let link objfiles output_name = Clflags.all_ccopts := !lib_ccopts @ !Clflags.all_ccopts; (* put user's opts first *) Clflags.dllibs := !lib_dllibs @ !Clflags.dllibs; (* put user's DLLs first *) + if !Clflags.custom_runtime then + Compenv.set_caml_standard_library_default (); if not !Clflags.custom_runtime then begin assert (!Clflags.global_string_constants = []); link_bytecode tolink output_name true diff --git a/bytecomp/dll.ml b/bytecomp/dll.ml index c8b280f9a012..528b963213f6 100644 --- a/bytecomp/dll.ml +++ b/bytecomp/dll.ml @@ -143,7 +143,8 @@ let synchronize_primitive num symb = let ld_conf_contents () = let path = ref [] in begin try - let ic = open_in (Filename.concat Config.standard_library "ld.conf") in + let ic = + open_in (Filename.concat Config.standard_library_effective "ld.conf") in begin try while true do path := input_line ic :: !path diff --git a/configure b/configure index d5d3d6a0a3d8..fb9f73efa9bf 100755 --- a/configure +++ b/configure @@ -785,6 +785,7 @@ build_os build_vendor build_cpu build +relocatable bindir_to_libdir ar_supports_response_files QS @@ -3328,6 +3329,7 @@ ocamltest_unix_impl="dummy" unix_library="" unix_directory="" bindir_to_libdir='' +relocatable=no # Information about the package @@ -3521,6 +3523,7 @@ LINEAR_MAGIC_NUMBER=Caml1999L035 + ## Generated files @@ -14953,6 +14956,17 @@ then : fi + for ac_header in unistd.h +do : + ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_unistd_h" = xyes +then : + printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h + printf "%s\n" "#define HAS_UNISTD 1" >>confdefs.h + +fi + +done ac_fn_c_check_header_compile "$LINENO" "pthread_np.h" "ac_cv_header_pthread_np_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_np_h" = xyes then : @@ -20932,6 +20946,7 @@ fi if test x"$enable_relative" = "xyes" then : + relocatable=yes eval "from=\"\$bindir\"" eval "to=\"\$libdir\"" diff --git a/configure.ac b/configure.ac index 0ac074aaf852..3ac691760a9c 100644 --- a/configure.ac +++ b/configure.ac @@ -84,6 +84,7 @@ ocamltest_unix_impl="dummy" unix_library="" unix_directory="" bindir_to_libdir='' +relocatable=no # Information about the package @@ -257,6 +258,7 @@ AC_SUBST([ocaml_libdir]) AC_SUBST([QS]) AC_SUBST([ar_supports_response_files]) AC_SUBST([bindir_to_libdir]) +AC_SUBST([relocatable]) ## Generated files @@ -1169,6 +1171,7 @@ AS_CASE([$host], [AC_CHECK_HEADERS([unistd.h],[AC_DEFINE([HAS_UNISTD], [1])])]) AC_CHECK_HEADER([math.h]) +AC_CHECK_HEADERS([unistd.h],[AC_DEFINE([HAS_UNISTD])]) AC_CHECK_HEADER([pthread_np.h],[AC_DEFINE([HAS_PTHREAD_NP_H], [1])]) AC_CHECK_HEADER([dirent.h], [AC_DEFINE([HAS_DIRENT], [1])], [], [#include ]) @@ -2645,6 +2648,7 @@ AS_IF([test x"$mandir" = x'${datarootdir}/man'], [mandir='${prefix}/man']) AS_IF([test x"$enable_relative" = "xyes"],[ + relocatable=yes OCAML_COMPUTE_BINDIR_TO_LIBDIR([bindir],[libdir],[bindir_to_libdir]) dnl Tests for the OCAML_COMPUTE_BINDIR_TO_LIBDIR macro dnl OCAML_COMPUTE_BINDIR_TO_LIBDIR_TESTS diff --git a/driver/compenv.ml b/driver/compenv.ml index 921c05655001..7fe010e52ab8 100644 --- a/driver/compenv.ml +++ b/driver/compenv.ml @@ -732,3 +732,10 @@ let parse_arguments ?(current=ref 0) argv f program = Printf.sprintf "Usage: %s \nOptions are:" program in Printf.printf "%s\n%s" help_msg err_msg; raise (Exit_with_status 0) + +let set_caml_standard_library_default () = + let symbol = "caml_standard_library_default" in + if not (List.exists (fun (name, _) -> name = symbol) + !global_string_constants) then + global_string_constants := + (symbol, Config.standard_library_effective) :: !global_string_constants diff --git a/driver/compenv.mli b/driver/compenv.mli index a5958554e76e..12d3bd9274a1 100644 --- a/driver/compenv.mli +++ b/driver/compenv.mli @@ -76,3 +76,7 @@ val process_deferred_actions : *) val parse_arguments : ?current:(int ref) -> string array ref -> Arg.anon_fun -> string -> unit + +(** Adds caml_standard_library_default to {!Clflags.global_string_constants} + if it isn't already present. *) +val set_caml_standard_library_default: unit -> unit diff --git a/runtime/caml/dynlink.h b/runtime/caml/dynlink.h index d5f7170e6fcb..c8a1d88cb0ba 100644 --- a/runtime/caml/dynlink.h +++ b/runtime/caml/dynlink.h @@ -41,9 +41,6 @@ extern void caml_build_primitive_table_builtin(void); /* Unload all the previously loaded shared libraries */ extern void caml_free_shared_libs(void); -/* Return the effective location of the standard library */ -extern char_os * caml_get_stdlib_location(void); - /* Parse ld.conf and add the lines read to caml_shared_libs_path */ extern char_os * caml_parse_ld_conf(void); diff --git a/runtime/caml/osdeps.h b/runtime/caml/osdeps.h index 6d414a325174..9e6a217c8268 100644 --- a/runtime/caml/osdeps.h +++ b/runtime/caml/osdeps.h @@ -142,6 +142,14 @@ CAMLextern clock_t caml_win32_clock(void); CAMLextern value caml_win32_xdg_defaults(void); +#define CAML_DIR_SEP T("\\") +#define Is_dir_separator(c) (c == '\\' || c == '/') + +#else + +#define CAML_DIR_SEP "/" +#define Is_dir_separator(c) (c == '/') + #endif /* _WIN32 */ /* Returns the current value of a counter that increments once per nanosecond. @@ -155,6 +163,18 @@ extern uint64_t caml_time_counter(void); extern void caml_init_os_params(void); +/* True if: + - dir equals "." + - dir equals ".." + - dir begins "./" + - dir begins "../" + The tests for null avoid the need to call strlen_os. */ +#define Is_relative_dir(dir) \ + (dir[0] == '.' \ + && (dir[1] == 0 \ + || Is_dir_separator(dir[1]) \ + || (dir[1] == '.' && (dir[2] == 0 || Is_dir_separator(dir[2]))))) + #endif /* CAML_INTERNALS */ #ifdef _WIN32 diff --git a/runtime/caml/s.h.in b/runtime/caml/s.h.in index bc6ced458da6..8c6b6b6af999 100644 --- a/runtime/caml/s.h.in +++ b/runtime/caml/s.h.in @@ -112,6 +112,10 @@ /* Define HAS_UNISTD if you have /usr/include/unistd.h. */ +#undef HAS_LIBGEN_H + +/* Define HAS_LIBGEN_H if you have /usr/include/libgen.h. */ + #undef HAS_DIRENT /* Define HAS_DIRENT if you have /usr/include/dirent.h and the result of diff --git a/runtime/caml/sys.h b/runtime/caml/sys.h index b6e6af870311..23f5671e988d 100644 --- a/runtime/caml/sys.h +++ b/runtime/caml/sys.h @@ -34,9 +34,15 @@ CAMLnoret CAMLextern void caml_sys_io_error (value); CAMLextern double caml_sys_time_unboxed(value); CAMLextern void caml_sys_init (char_os * exe_name, char_os ** argv); +CAMLextern void caml_locate_standard_library (const char_os *); CAMLnoret CAMLextern void caml_do_exit (int); +extern char_os * caml_standard_library_default; +extern char_os * caml_standard_library_relative; +extern char_os * caml_standard_library; +extern char_os *_Atomic caml_relative_root_dir; + #ifdef __cplusplus } #endif diff --git a/runtime/dynlink.c b/runtime/dynlink.c index c06f8adb9aac..28579e175d5e 100644 --- a/runtime/dynlink.c +++ b/runtime/dynlink.c @@ -40,6 +40,7 @@ #include "caml/signals.h" #include "caml/intext.h" #include "caml/startup.h" +#include "caml/sys.h" #include "build_config.h" @@ -88,7 +89,7 @@ CAMLexport char_os * caml_get_stdlib_location(void) char_os * stdlib; stdlib = caml_secure_getenv(T("OCAMLLIB")); if (stdlib == NULL) stdlib = caml_secure_getenv(T("CAMLLIB")); - if (stdlib == NULL) stdlib = OCAML_STDLIB_DIR; + if (stdlib == NULL) stdlib = caml_standard_library; return stdlib; } diff --git a/runtime/startup_byt.c b/runtime/startup_byt.c index ffb4eb0bebdd..486b83662b93 100644 --- a/runtime/startup_byt.c +++ b/runtime/startup_byt.c @@ -383,9 +383,7 @@ static void do_print_config(void) /* Print the runtime configuration */ printf("version: %s\n", OCAML_VERSION_STRING); printf("standard_library_default: %s\n", - caml_stat_strdup_of_os(OCAML_STDLIB_DIR)); - printf("standard_library: %s\n", - caml_stat_strdup_of_os(caml_get_stdlib_location())); + caml_stat_strdup_of_os(caml_standard_library_default)); printf("int_size: %d\n", 8 * (int)sizeof(value)); printf("word_size: %d\n", 8 * (int)sizeof(value) - 1); printf("os_type: %s\n", OCAML_OS_TYPE); @@ -486,11 +484,14 @@ CAMLexport void caml_main(char_os **argv) With -custom, we have an executable that is ocamlrun itself concatenated with the bytecode. So, if the attempt with argv[0] failed, it is worth trying again with executable_name. */ - if (fd < 0 && (proc_self_exe = caml_executable_name()) != NULL) { + proc_self_exe = caml_executable_name(); + if (fd < 0 && proc_self_exe != NULL) { exe_name = proc_self_exe; fd = caml_attempt_open(&exe_name, &trail, 0); } + caml_locate_standard_library(proc_self_exe ? proc_self_exe : exe_name); + if (fd < 0) { pos = parse_command_line(argv); if (caml_params->print_config) { @@ -622,6 +623,8 @@ CAMLexport value caml_startup_code_exn( exe_name = caml_executable_name(); if (exe_name == NULL) exe_name = caml_search_exe_in_path(argv[0]); + caml_locate_standard_library(exe_name); + Caml_state->external_raise = NULL; /* Setup signal handling */ caml_init_signals(); diff --git a/runtime/stdlib.c b/runtime/stdlib.c new file mode 100644 index 000000000000..0be10ae51df5 --- /dev/null +++ b/runtime/stdlib.c @@ -0,0 +1,22 @@ +/**************************************************************************/ +/* */ +/* OCaml */ +/* */ +/* David Allsopp, OCaml Labs, Cambridge. */ +/* */ +/* Copyright 2021 David Allsopp Ltd. */ +/* */ +/* All rights reserved. This file is distributed under the terms of */ +/* the GNU Lesser General Public License version 2.1, with the */ +/* special exception on linking described in the file LICENSE. */ +/* */ +/**************************************************************************/ + +#include "caml/misc.h" +#include "build_config.h" + +#if defined(OCAML_STDLIB_DIR_REL) +char_os * caml_standard_library_default = OCAML_STDLIB_DIR_REL; +#else +char_os * caml_standard_library_default = OCAML_STDLIB_DIR; +#endif diff --git a/runtime/sys.c b/runtime/sys.c index 3356664ecdbd..d105d5357a2f 100644 --- a/runtime/sys.c +++ b/runtime/sys.c @@ -67,6 +67,9 @@ #include "caml/major_gc.h" #include "caml/shared_heap.h" +char_os * caml_standard_library = NULL; +char_os *_Atomic caml_relative_root_dir = NULL; + CAMLexport char * caml_strerror(int errnum, char * buf, size_t buflen) { #ifdef _WIN32 @@ -700,6 +703,30 @@ CAMLprim value caml_sys_const_backend_type(value unit) { return Val_int(1); /* Bytecode backed */ } + +CAMLprim value caml_sys_get_stdlib_dirs(value unit) +{ + CAMLparam0(); + CAMLlocal4(result, def, eff, root_dir); +#ifdef NATIVE_CODE + if (caml_standard_library == NULL) + caml_locate_standard_library(caml_params->exe_name); +#endif + def = caml_copy_string_of_os(caml_standard_library_default); + if (caml_relative_root_dir != NULL) { + root_dir = caml_copy_string_of_os(caml_relative_root_dir); + root_dir = caml_alloc_some(root_dir); + } else { + root_dir = Val_none; + } + eff = caml_copy_string_of_os(caml_standard_library); + result = caml_alloc_small(3, 0); + Field(result, 0) = def; + Field(result, 1) = eff; + Field(result, 2) = root_dir; + CAMLreturn(result); +} + CAMLprim value caml_sys_get_config(value unit) { CAMLparam0 (); /* unit is unused */ diff --git a/runtime/unix.c b/runtime/unix.c index e19d5a26a86d..110c55c29dbb 100644 --- a/runtime/unix.c +++ b/runtime/unix.c @@ -54,6 +54,9 @@ #else #include #endif +#ifdef HAS_LIBGEN_H +#include +#endif #ifdef __APPLE__ #include #endif @@ -541,3 +544,70 @@ void caml_plat_mem_unmap(void* mem, uintnat size) if (munmap(mem, size) != 0) CAMLassert(0); } + +static char * caml_dirname (const char * path) +{ +#ifdef HAS_LIBGEN_H + char *dir, *res; + dir = caml_stat_strdup(path); + res = caml_stat_strdup(dirname(dir)); + caml_stat_free(dir); + return res; +#else + /* See Filename.generic_dirname */ + size_t n = strlen(path) - 1; + char *res; + if (n < 0) /* path is "" */ + return caml_stat_strdup("."); + while (n >= 0 && path[n] == '/') + n--; + if (n < 0) /* path is entirely slashes */ + return caml_stat_strdup("/"); + while (n >= 0 && path[n] != '/') + n--; + if (n < 0) /* path is relative */ + return caml_stat_strdup("."); + while (n >= 0 && path[n] == '/') + n--; + if (n < 0) /* path is a file at root */ + return caml_stat_strdup("/"); + /* n is the _index_ of the last character of the dirname */ + res = caml_stat_alloc(n + 2); + memcpy(res, path, n + 1); + res[n + 1] = 0; + return res; +#endif +} + +CAMLextern void caml_locate_standard_library (const char *exe_name) +{ + if (Is_relative_dir(caml_standard_library_default)) { + char * candidate = NULL; + char * root = caml_dirname(exe_name); + /* Determine the standard library default relative to exe_name */ + if (!atomic_compare_exchange_weak(&caml_relative_root_dir, + &candidate, root)) { + caml_stat_free(root); + } else { + candidate = caml_stat_strconcat_os(3, caml_relative_root_dir, + CAML_DIR_SEP, + caml_standard_library_default); +#ifndef HAS_REALPATH + caml_standard_library = candidate; +#else + char * resolved_candidate = realpath(candidate, NULL); + /* If realpath fails, use the non-normalised path for error messages. */ + if (resolved_candidate == NULL) { + caml_standard_library = candidate; + } else { + caml_stat_free(candidate); + /* caml_realpath uses malloc */ + caml_standard_library = caml_stat_strdup_os(resolved_candidate); + free(resolved_candidate); + } +#endif + } + } else { + caml_standard_library = caml_standard_library_default; + } +} diff --git a/runtime/win32.c b/runtime/win32.c index 5b324da96189..85e0a2731f37 100644 --- a/runtime/win32.c +++ b/runtime/win32.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "caml/alloc.h" #include "caml/codefrag.h" #include "caml/fail.h" @@ -1291,3 +1292,79 @@ value caml_win32_xdg_defaults(void) CAMLreturn(result); } + +CAMLextern void caml_locate_standard_library (const wchar_t *exe_name) +{ + if (Is_relative_dir(caml_standard_library_default)) { + LPWSTR root, basename, candidate = NULL; + DWORD buf_len, l; + HANDLE h; + l = GetFullPathName(exe_name, 0, NULL, NULL); + if (l == 0) { + /* Impossible thing: the path returned by GetModuleFileName can't be + parsed by GetFullPathName */ + caml_standard_library = caml_standard_library_default; + return; + } +again1: + CAMLassert(l != 0); + buf_len = l; + root = caml_stat_alloc(buf_len * sizeof(wchar_t)); + l = GetFullPathName(exe_name, buf_len, root, &basename); + if (l > buf_len) { + caml_stat_free(root); + goto again1; + } + CAMLassert(basename != root && *(basename - 1) == '/'); + /* Make root the dirname */ + *(basename - 1) = 0; + /* Determine the standard library default relative to exe_name */ + if (!atomic_compare_exchange_weak(&caml_relative_root_dir, + &candidate, root)) { + caml_stat_free(root); + } else { + LPWSTR resolved_candidate = NULL; + candidate = caml_stat_strconcat_os(3, caml_relative_root_dir, + CAML_DIR_SEP, + caml_standard_library_default); + h = CreateFile(candidate, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h != INVALID_HANDLE_VALUE) { + l = GetFinalPathNameByHandle(h, NULL, 0, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); +again2: + buf_len = l; + resolved_candidate = caml_stat_alloc(buf_len * sizeof(wchar_t)); + l = GetFinalPathNameByHandle(h, resolved_candidate, buf_len, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (l > buf_len) { + caml_stat_free(resolved_candidate); + goto again2; + } + /* GetFinalPathNameByHandle always returns \\?\ which needs stripping. + UNC paths are returned as \\?\UNC\ - in this case, reuse the \\ from + \\?\. */ + CAMLassert(l > 4 && resolved_candidate[0] == '\\' + && resolved_candidate[1] == '\\' + && resolved_candidate[2] == '?' + && resolved_candidate[3] == '\\'); + if (l >= 8 && resolved_candidate[4] == 'U' + && resolved_candidate[5] == 'N' + && resolved_candidate[6] == 'C' + && resolved_candidate[7] == '\\') + wmemmove_s(resolved_candidate + 2, l - 1, + resolved_candidate + 8, l - 7); + else + wmemmove_s(resolved_candidate, l + 1, + resolved_candidate + 4, l - 3); + caml_stat_free(candidate); + caml_standard_library = resolved_candidate; + } else { + caml_standard_library = candidate; + } + } + } else { + caml_standard_library = caml_standard_library_default; + } +} diff --git a/tools/ocamlmklib.ml b/tools/ocamlmklib.ml index f6b2a2d16ec9..1082a208d3c7 100644 --- a/tools/ocamlmklib.ml +++ b/tools/ocamlmklib.ml @@ -25,10 +25,8 @@ let mklib out files opts = Printf.sprintf "link -lib -nologo %s-out:%s %s %s" machine out opts files else Printf.sprintf "%s rcs %s %s %s" Config.ar out opts files -(* PR#4783: under Windows, don't use absolute paths because we do - not know where the binary distribution will be installed. *) let compiler_path name = - if Sys.os_type = "Win32" then name else Filename.concat Config.bindir name + Filename.concat Config.bindir name let bytecode_objs = ref [] (* .cmo,.cma,.ml,.mli files to pass to ocamlc *) and native_objs = ref [] (* .cmx,.ml,.mli files to pass to ocamlopt *) diff --git a/utils/config.common.ml.in b/utils/config.common.ml.in index 3603fe6c60c7..459b18d9d9e7 100644 --- a/utils/config.common.ml.in +++ b/utils/config.common.ml.in @@ -27,7 +27,7 @@ let standard_library = try Sys.getenv "CAMLLIB" with Not_found -> - standard_library_default + standard_library_effective let exec_magic_number = {magic|@EXEC_MAGIC_NUMBER@|magic} (* exec_magic_number is duplicated in runtime/caml/exec.h *) @@ -73,7 +73,7 @@ let configuration_variables () = let p_bool x v = (x, Bool v) in [ p "version" version; - p "standard_library_default" standard_library_default; + p "standard_library_default" standard_library_effective; p "standard_library" standard_library; p "ccomp_type" ccomp_type; p "c_compiler" c_compiler; diff --git a/utils/config.fixed.ml b/utils/config.fixed.ml index 49194b9518c3..16d2cc7c19d1 100644 --- a/utils/config.fixed.ml +++ b/utils/config.fixed.ml @@ -22,6 +22,8 @@ let boot_cannot_call s = "/ The boot compiler should not call " ^ s let bindir = "/tmp" let standard_library_default = "/tmp" +let standard_library_effective = "/tmp" +let standard_library_relative = false let ccomp_type = "n/a" let c_compiler = boot_cannot_call "the C compiler" let c_output_obj = "" diff --git a/utils/config.generated.ml.in b/utils/config.generated.ml.in index 46f8d611ce9a..46ab22ccef56 100644 --- a/utils/config.generated.ml.in +++ b/utils/config.generated.ml.in @@ -18,9 +18,23 @@ (* This file is included in config_main.ml during the build rather than compiled on its own *) -let bindir = {@QS@|@ocaml_bindir@|@QS@} - -let standard_library_default = {@QS@|@ocaml_libdir@|@QS@} +external stdlib_dirs : unit -> string * string * string option + = "caml_sys_get_stdlib_dirs" + +let (standard_library_default, + standard_library_effective, + relative_root_dir) = stdlib_dirs () + +let bindir = + let default = + if {@QS@|@bindir_to_libdir@|@QS@} = "" then + {@QS@|@bindir@|@QS@} + else + "" + in + Option.value ~default relative_root_dir + +let standard_library_relative = relative_root_dir <> None let ccomp_type = {@QS@|@ccomptype@|@QS@} let c_compiler = {@QS@|@CC@|@QS@} diff --git a/utils/config.mli b/utils/config.mli index e9b24a18d905..d179a602e6b3 100644 --- a/utils/config.mli +++ b/utils/config.mli @@ -24,10 +24,31 @@ val version: string (** The current version number of the system *) val bindir: string -(** The directory containing the binary programs *) +(** The directory containing the binary programs. If the compiler was configured + with [--enable-relative] then this will be the directory containing the + currently executing runtime. *) + +val standard_library_default: string +(** The configured value for the directory containing the standard libraries. + May be a relative path if the compiler was configured with + [--enable-relative]. + + @since 5.1 *) + +val standard_library_effective: string +(** The standard library directory, computed taking {!standard_library_relative} + and {!standard_library_default} into account, but not taking CAMLLIB or + OCAMLLIB into account. + + @since 5.1 *) + +val standard_library_relative: bool +(** Whether {!standard_library_effective} is computed relative to the runtime. + + @since 5.1 *) val standard_library: string -(** The directory containing the standard libraries *) +(** The effective directory containing the standard libraries. *) val ccomp_type: string (** The "kind" of the C compiler, assembler and linker used: one of