diff --git a/tslang/test/tester/defaultlib-collector.cmake b/tslang/test/tester/defaultlib-collector.cmake index 8d13609ee..67e9179c1 100644 --- a/tslang/test/tester/defaultlib-collector.cmake +++ b/tslang/test/tester/defaultlib-collector.cmake @@ -138,6 +138,32 @@ if(MODE STREQUAL "jit") if(NOT WIN32) run_program("JIT, default library, runtime not passed" "${WORK_DIR}/alone" "${TSLANG}" --emit=jit ${common} "${alone}") + + # Laid out as the release package is, the runtime next to tslang, and run from another + # folder with no --tslang-lib-path: tslang finds it in its own folder. Without it there, + # a clear error rather than the loader's "undefined symbol: GC_malloc" mid-run. A hard + # link, so /proc/self/exe names the package folder (a symlink resolves to the build). + set(package "${WORK_DIR}/package") + file(REMOVE_RECURSE "${package}") + file(MAKE_DIRECTORY "${package}") + file(CREATE_LINK "${TSLANG}" "${package}/tslang" COPY_ON_ERROR) + set(no_lib_path ${common}) + list(FILTER no_lib_path EXCLUDE REGEX "^--tslang-lib-path=") + + execute_process(COMMAND "${package}/tslang" --emit=jit ${no_lib_path} "${alone}" + WORKING_DIRECTORY "${WORK_DIR}/alone" + OUTPUT_VARIABLE out + ERROR_VARIABLE err + RESULT_VARIABLE status) + if(status EQUAL 0 OR NOT err MATCHES "needs the GC runtime, libTypeScriptRuntime.so") + message(FATAL_ERROR "JIT with no runtime to find: exit ${status}\n${out}\n${err}") + endif() + message(STATUS "JIT with no runtime to find: refused") + + # the build's runtime is a symlink to its versioned file + file(COPY "${runtime}" DESTINATION "${package}" FOLLOW_SYMLINK_CHAIN) + run_program("JIT, default library, runtime next to tslang" "${WORK_DIR}/alone" + "${package}/tslang" --emit=jit ${no_lib_path} "${alone}") endif() elseif(MODE STREQUAL "compile") run("--emit=exe alone, default library" "${WORK_DIR}/alone" diff --git a/tslang/tslang/jit.cpp b/tslang/tslang/jit.cpp index 50b012e1a..96c0b8599 100644 --- a/tslang/tslang/jit.cpp +++ b/tslang/tslang/jit.cpp @@ -74,6 +74,7 @@ std::string mergeWithDefaultLibPath(std::string, std::string); std::string getDefaultLibSubDir(bool, bool, const char *, const llvm::Triple &); std::string getDefaultLibBinary(bool, const CompileOptions &, const llvm::Triple &); std::string makeAbsolutePath(std::string); +std::string getExecutablePath(const char *); int registerMLIRDialects(mlir::ModuleOp); std::function getTransformer(bool, int, int, CompileOptions&); @@ -649,39 +650,46 @@ static int prepareJitProcess(CompileOptions &compileOptions, bool nativeImage) return llvm::sys::path::stem(libPath).contains_insensitive("TypeScriptRuntime"); }); - std::string pathTypeScriptLib("../lib/" LIB_NAME "TypeScriptRuntime." LIB_EXT); if (compileOptions.needsGCRuntime() && !hasTypeScriptRuntime && !nativeImage) { - auto absPath3 = makeAbsolutePath(mergeWithDefaultLibPath(getTslangLibPath(), LIB_NAME "TypeScriptRuntime." LIB_EXT)); - if (absPath3.empty()) + // --tslang-lib-path (TSLANG_LIB_PATH), then ../lib and the current folder, then beside + // tslang itself: its own folder - where the release package puts it, and the Windows build + // its bin folder - and ../lib, where the Linux build does. A run started from a project + // folder finds it only there. + llvm::SmallString<256> exeDir(getExecutablePath("tslang")); + llvm::sys::path::remove_filename(exeDir); + llvm::SmallString<256> exeRuntimePath(exeDir); + llvm::sys::path::append(exeRuntimePath, LIB_NAME "TypeScriptRuntime." LIB_EXT); + llvm::SmallString<256> exeLibRuntimePath(exeDir); + llvm::sys::path::append(exeLibRuntimePath, "..", "lib", LIB_NAME "TypeScriptRuntime." LIB_EXT); + + std::string runtimePath; + for (auto candidate : {mergeWithDefaultLibPath(getTslangLibPath(), LIB_NAME "TypeScriptRuntime." LIB_EXT), + std::string("../lib/" LIB_NAME "TypeScriptRuntime." LIB_EXT), + std::string(LIB_NAME "TypeScriptRuntime." LIB_EXT), + exeRuntimePath.str().str(), + exeLibRuntimePath.str().str()}) { - auto absPath = makeAbsolutePath(pathTypeScriptLib); - if (absPath.empty()) - { - pathTypeScriptLib = LIB_NAME "TypeScriptRuntime." LIB_EXT; - auto absPath2 = makeAbsolutePath(pathTypeScriptLib); - if (absPath2.empty()) - { - /* - llvm::WithColor::error(llvm::errs(), "tslang") << "JIT initialization failed. Missing GC library. Did you forget to provide it via " - "'--shared-libs=" LIB_NAME "TypeScriptRuntime." LIB_EXT "'? or you can switch it off by using '-mm=none'\n"; - return -1; - */ - } - else - { - clSharedLibs.push_back(absPath2); - } - } - else + runtimePath = makeAbsolutePath(candidate); + if (!runtimePath.empty()) { - clSharedLibs.push_back(absPath); + break; } - } - else + } + + // Not left to fail later: the default library and the program call GC_malloc & co., and + // with no runtime loaded that surfaces as an unresolved symbol (on Linux a "symbol lookup + // error" from the loader in the middle of the run) that never names the missing file. + if (runtimePath.empty()) { - clSharedLibs.push_back(absPath3); + llvm::WithColor::error(llvm::errs(), "tslang") + << "-mm=gc needs the GC runtime, " LIB_NAME "TypeScriptRuntime." LIB_EXT ", and it is not " + << "next to tslang, in the current folder or in --tslang-lib-path. Pass its folder with " + << "--tslang-lib-path, or the file with --shared-libs.\n"; + return -1; } + + clSharedLibs.push_back(runtimePath); } // After the runtime: on Linux the default library leaves GC_* undefined and takes them from