From 2641416d9e1716ca4d8f3d72c98e83ea7060ea7b Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 6 Oct 2026 17:36:38 +0100 Subject: [PATCH] Default library binaries split by target triple, then build, then memory model defaultlib/{lib,dll}/////{debug,release}/{gc,rc,none}/ for every target (the host included), each segment the canonical name LLVM gives the parsed triple component, so i686/i386, an Android API level or an MSVC version share one tree. The declarations and everything else stay at the defaultlib root, shared by every build, so Android no longer needs a tree of its own. getDefaultLibSubDir moves from Defines.h (which the runtime includes without LLVM) to defaultlib.cpp and takes the triple; the linker, the JIT, --install-default-lib and the new --print-default-lib-dir= all call it. Build scripts and CMake templates ask --print-default-lib-dir instead of spelling the layout out; test-default-lib-dir pins it. check-x86-libs.sh: the stand-in default-library trees get a lib.d.ts, missing since #371 made an unreadable reference an error. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/create-release.yml | 23 ++++++------- docs/how/cmake_bgfx/README.md | 3 +- docs/how/cmake_bgfx/cmake/LocateTSLang.cmake | 22 +++++++----- docs/how/cmake_dll/CMakeLists.txt | 20 +++++++---- docs/how/cmake_dll/README.md | 3 +- docs/how/cmake_tslang/CMakeLists.txt | 20 +++++++---- docs/how/cmake_tslang/README.md | 5 +-- docs/memory-models.md | 3 +- tslang/build_tslang_defaultlib_debug.bat | 4 +-- tslang/build_tslang_defaultlib_release.bat | 4 +-- tslang/include/TypeScript/Defines.h | 32 ++++++++--------- .../include/TypeScript/VSCodeTemplate/Files.h | 23 ++++++++----- tslang/test/check-x86-libs.sh | 28 +++++++++------ tslang/test/check-x86-run.sh | 11 +++--- tslang/test/tester/CMakeLists.txt | 7 +++- tslang/test/tester/android-link.cmake | 13 ++++--- tslang/test/tester/default-lib-dir.cmake | 34 +++++++++++++++++++ tslang/test/tester/defaultlib-collector.cmake | 24 ++++++------- tslang/tslang/defaultlib.cpp | 24 +++++++++++-- tslang/tslang/exe.cpp | 17 ++++------ tslang/tslang/jit.cpp | 4 ++- tslang/tslang/tslang.cpp | 23 +++++++++++++ 22 files changed, 231 insertions(+), 116 deletions(-) create mode 100644 tslang/test/tester/default-lib-dir.cmake diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 315db26b9..845e0b5b3 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -240,7 +240,7 @@ jobs: continue-on-error: false working-directory: ${{github.workspace}}/__build/tslang/msbuild/x64/release env: - TSLANG_ANDROID_DEFAULT_LIB_PATH: ${{github.workspace}}/TypeScriptCompilerDefaultLib/__build/android/x86_64 + TSLANG_ANDROID_DEFAULT_LIB_PATH: ${{github.workspace}}/TypeScriptCompilerDefaultLib/__build run: | $out = ctest -C ${{ env.BUILD_TYPE }} -R "test-compile-android-" --output-on-failure 2>&1 | Out-String $out @@ -251,28 +251,24 @@ jobs: - name: Create Zip of Windows Asset working-directory: ${{github.workspace}}/__build # Flatten the compiler/runtime binaries into the archive root, and add the - # default library as a whole tree so its per-build, per-memory-model subfolders - # are preserved: - # defaultlib/{dll,lib}/{debug,release}/{gc,rc,none}, - # defaultlib/*.d.ts, defaultlib/generics/ + # default library as a whole tree so its per-target, per-build, per-memory-model + # subfolders are preserved, Android's among them (tslang --print-default-lib-dir names one): + # defaultlib/{dll,lib}/////{debug,release}/{gc,rc,none}, + # defaultlib/*.d.ts, defaultlib/generics/ (shared by every target) # plus the shared collector in its own folder, because its import library is also named # gc.lib: gcdll/gc.lib + gcdll/gc.dll, for programs that load a tslang shared library. # tsbindgen looks for clang's resource directory next to itself as lib/clang/ # (findResourceDir's "release package" candidate), so stage that layout too - its headers # only: the official LLVM package also puts compiler-rt's libraries under # lib/clang//lib, which neither tsbindgen nor tslang uses. - # Android, per ABI (arm64-v8a, x86_64): android//defaultlib/... (--default-lib-path= - # android/) and android//lib/{libgc.a,libTypeScriptAsyncRuntime.a} (--gc-lib-path - # and --tslang-lib-path). + # Android, per ABI (arm64-v8a, x86_64): android//lib/{libgc.a,libTypeScriptAsyncRuntime.a} + # (--gc-lib-path and --tslang-lib-path); its default library is in the defaultlib tree above. run: | foreach ($abi in 'arm64-v8a', 'x86_64') { $dest = ".\android_stage\android\$abi" New-Item -ItemType Directory -Force -Path "$dest\lib" | Out-Null - # moved, not copied: the default library's __build, packaged whole below, would carry it twice - Move-Item -Path "..\TypeScriptCompilerDefaultLib\__build\android\$abi\defaultlib" -Destination $dest -ErrorAction Stop Copy-Item -Path "..\3rdParty\gc\android\$abi\release\lib\libgc.a", ".\tslang-runtime\release\android\$abi\libTypeScriptAsyncRuntime.a" -Destination "$dest\lib" -ErrorAction Stop } - Remove-Item -Recurse -Force -Path "..\TypeScriptCompilerDefaultLib\__build\android" -ErrorAction Stop New-Item -ItemType Directory -Force -Path .\gcdll_stage\gcdll | Out-Null Copy-Item -Path ..\3rdParty\gcdll\x64\release\lib\gc.lib, ..\3rdParty\gcdll\x64\release\bin\gc.dll -Destination .\gcdll_stage\gcdll -ErrorAction Stop Get-ChildItem -Path ..\3rdParty\llvm\x64\release\lib\clang -Directory | ForEach-Object { $dest = ".\resourcedir_stage\lib\clang\$($_.Name)"; New-Item -ItemType Directory -Force -Path $dest | Out-Null; Copy-Item -Path (Join-Path $_.FullName "include") -Destination $dest -Recurse -ErrorAction Stop } @@ -453,8 +449,9 @@ jobs: cp ./gc/ninja/release/libgc.a ./__stage/ cp ./tslang/ninja/release/lib/libTypeScriptAsyncRuntime.a ./__stage/ cp ../3rdParty/llvm/release/bin/wasm-ld ./__stage/ - # Add the default library as a whole tree so its per-build, per-memory-model - # subfolders are preserved: defaultlib/{dll,lib}/{debug,release}/{gc,rc,none}, + # Add the default library as a whole tree so its per-target, per-build, per-memory-model + # subfolders are preserved: + # defaultlib/{dll,lib}/////{debug,release}/{gc,rc,none}, # defaultlib/*.d.ts, defaultlib/generics/ cp -r ../TypeScriptCompilerDefaultLib/__build/defaultlib ./__stage/ # tsbindgen looks for clang's resource directory next to itself as lib/clang/ diff --git a/docs/how/cmake_bgfx/README.md b/docs/how/cmake_bgfx/README.md index e0c082c4d..1381862e1 100644 --- a/docs/how/cmake_bgfx/README.md +++ b/docs/how/cmake_bgfx/README.md @@ -109,7 +109,8 @@ build matching the model it was compiled with. One variable drives both: cmake --preset default -DTSLANG_MEMORY_MODEL=rc ``` -It selects `defaultlib/lib//` as the link directory and adds `-mm=` +It selects `defaultlib/lib//////` (as +`tslang --print-default-lib-dir=lib` names it) as the link directory and adds `-mm=` to the compile flags, so the two cannot disagree; configuring fails if that model has not been built. Valid values are `gc` (default), `rc` and `none`; only `gc` links Boehm. diff --git a/docs/how/cmake_bgfx/cmake/LocateTSLang.cmake b/docs/how/cmake_bgfx/cmake/LocateTSLang.cmake index 52d4e1147..482b7f63d 100644 --- a/docs/how/cmake_bgfx/cmake/LocateTSLang.cmake +++ b/docs/how/cmake_bgfx/cmake/LocateTSLang.cmake @@ -113,18 +113,24 @@ function(setup_tslang_link_paths) message(FATAL_ERROR "setup_tslang_link_paths: TSLANG_PREFIX is not set") endif() - # The compiled default lib is split per build mode (debug/release) and then per memory - # model (gc/rc/none): a default lib built for one model allocates the way that model - # allocates, so linking it into a program built for another is the mismatch the compiler - # refuses to paper over. TSLANG_MEMORY_MODEL picks both this directory and the -mm= flag. + # The compiled default lib is split per target, then per build mode (debug/release), then + # per memory model (gc/rc/none): a default lib built for one model allocates the way that + # model allocates, so linking it into a program built for another is the mismatch the + # compiler refuses to paper over. TSLANG_MEMORY_MODEL picks both this directory and the -mm= + # flag; tslang names the directory (--print-default-lib-dir), so the layout is not spelled + # out here. if(CMAKE_BUILD_TYPE STREQUAL "Release") - set(_defaultlib_config "release") + set(_defaultlib_build_flag "") else() - set(_defaultlib_config "debug") + set(_defaultlib_build_flag "--di") endif() - set(_defaultlib_dir - "${TSLANG_BIN_DIR}/defaultlib/lib/${_defaultlib_config}/${TSLANG_MEMORY_MODEL}") + execute_process(COMMAND "${CMAKE_TSLANG_COMPILER}" --print-default-lib-dir=lib ${_defaultlib_build_flag} + -mm=${TSLANG_MEMORY_MODEL} + OUTPUT_VARIABLE _defaultlib_subdir + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + set(_defaultlib_dir "${TSLANG_BIN_DIR}/${_defaultlib_subdir}") if(NOT IS_DIRECTORY "${_defaultlib_dir}") message(FATAL_ERROR "No default library built for -mm=${TSLANG_MEMORY_MODEL}: ${_defaultlib_dir} " diff --git a/docs/how/cmake_dll/CMakeLists.txt b/docs/how/cmake_dll/CMakeLists.txt index 72b88a6ff..12b1b0525 100644 --- a/docs/how/cmake_dll/CMakeLists.txt +++ b/docs/how/cmake_dll/CMakeLists.txt @@ -11,21 +11,27 @@ enable_language(TSLANG) # Include folders include_directories(${CMAKE_TSLANG_DIR}/defaultlib) -# The compiled default lib is split into per-build subfolders (debug/release) and then per -# memory model (gc/rc/none); pick the pair matching this build, so that the CRT, the allocator -# and the default-lib binaries all agree. A library built for one model cannot be linked into -# a program built for another. +# The compiled default lib is split per target, then per build (debug/release), then per memory +# model (gc/rc/none); pick the one matching this build, so that the CRT, the allocator and the +# default-lib binaries all agree. A library built for one model cannot be linked into a program +# built for another. tslang names the folder (--print-default-lib-dir), so this never spells out +# the layout. if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(TSLANG_DEFAULTLIB_BUILD "release") + set(TSLANG_DEFAULTLIB_BUILD_FLAG "") else() - set(TSLANG_DEFAULTLIB_BUILD "debug") + set(TSLANG_DEFAULTLIB_BUILD_FLAG "--di") endif() set(TSLANG_MEMORY_MODEL "gc" CACHE STRING "Memory model of compiled code: gc, rc or none") set_property(CACHE TSLANG_MEMORY_MODEL PROPERTY STRINGS gc rc none) +execute_process(COMMAND "${CMAKE_TSLANG_COMPILER}" --print-default-lib-dir=lib ${TSLANG_DEFAULTLIB_BUILD_FLAG} -mm=${TSLANG_MEMORY_MODEL} + OUTPUT_VARIABLE TSLANG_DEFAULTLIB_SUBDIR + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Lib folders -link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/defaultlib/lib/${TSLANG_DEFAULTLIB_BUILD}/${TSLANG_MEMORY_MODEL}) +link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/${TSLANG_DEFAULTLIB_SUBDIR}) # set options if (CMAKE_BUILD_TYPE STREQUAL "Release") diff --git a/docs/how/cmake_dll/README.md b/docs/how/cmake_dll/README.md index 2c5a2abc1..45b471782 100644 --- a/docs/how/cmake_dll/README.md +++ b/docs/how/cmake_dll/README.md @@ -52,7 +52,8 @@ the build matching the model it was compiled with. One variable drives both: cmake --preset default -DTSLANG_MEMORY_MODEL=rc ``` -It selects `defaultlib/lib//` as the link directory and adds `-mm=` +It selects `defaultlib/lib//////` (as +`tslang --print-default-lib-dir=lib` names it) as the link directory and adds `-mm=` to the compile flags, so the two cannot disagree. Valid values are `gc` (default), `rc` and `none`; only `gc` links Boehm. diff --git a/docs/how/cmake_tslang/CMakeLists.txt b/docs/how/cmake_tslang/CMakeLists.txt index 2138a6e90..6654562c5 100644 --- a/docs/how/cmake_tslang/CMakeLists.txt +++ b/docs/how/cmake_tslang/CMakeLists.txt @@ -11,14 +11,15 @@ enable_language(TSLANG) # Include folders include_directories(${CMAKE_TSLANG_DIR}/defaultlib) -# The compiled default lib is split into per-build subfolders (debug/release) and then per -# memory model (gc/rc/none); pick the pair matching this build, so that the CRT, the allocator -# and the default-lib binaries all agree. A library built for one model cannot be linked into -# a program built for another. +# The compiled default lib is split per target, then per build (debug/release), then per memory +# model (gc/rc/none); pick the one matching this build, so that the CRT, the allocator and the +# default-lib binaries all agree. A library built for one model cannot be linked into a program +# built for another. tslang names the folder (--print-default-lib-dir), so this never spells out +# the layout. if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(TSLANG_DEFAULTLIB_BUILD "release") + set(TSLANG_DEFAULTLIB_BUILD_FLAG "") else() - set(TSLANG_DEFAULTLIB_BUILD "debug") + set(TSLANG_DEFAULTLIB_BUILD_FLAG "--di") endif() set(TSLANG_MEMORY_MODEL "gc" CACHE STRING "Memory model of compiled code: gc, rc or none") @@ -30,8 +31,13 @@ set_property(CACHE TSLANG_MEMORY_MODEL PROPERTY STRINGS gc rc none) # Two collectors free each other's live objects. See docs/memory-models.md. option(TSLANG_SHARED_GC "The program loads tslang shared libraries (gc: share one collector)" OFF) +execute_process(COMMAND "${CMAKE_TSLANG_COMPILER}" --print-default-lib-dir=lib ${TSLANG_DEFAULTLIB_BUILD_FLAG} -mm=${TSLANG_MEMORY_MODEL} + OUTPUT_VARIABLE TSLANG_DEFAULTLIB_SUBDIR + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Lib folders -link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/defaultlib/lib/${TSLANG_DEFAULTLIB_BUILD}/${TSLANG_MEMORY_MODEL}) +link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/${TSLANG_DEFAULTLIB_SUBDIR}) # set options if (CMAKE_BUILD_TYPE STREQUAL "Release") diff --git a/docs/how/cmake_tslang/README.md b/docs/how/cmake_tslang/README.md index 1ebf0926f..fb8a80977 100644 --- a/docs/how/cmake_tslang/README.md +++ b/docs/how/cmake_tslang/README.md @@ -52,7 +52,8 @@ the build matching the model it was compiled with. One variable drives both: cmake --preset default -DTSLANG_MEMORY_MODEL=rc ``` -It selects `defaultlib/lib//` as the link directory and adds `-mm=` +It selects `defaultlib/lib//////` (as +`tslang --print-default-lib-dir=lib` names it) as the link directory and adds `-mm=` to the compile flags, so the two cannot disagree. Valid values are `gc` (default), `rc` and `none`; only `gc` links Boehm. @@ -69,7 +70,7 @@ cmake --preset default -DTSLANG_SHARED_GC=ON - Windows: links `gcdll/gc.lib` (gc.dll's import library) instead of the static `gc.lib`, and copies `gc.dll` beside the program. Ship it with `TypeScriptDefaultLib.dll` - (`defaultlib/dll//gc`) and your libraries. + (`defaultlib/dll//////gc`) and your libraries. - Linux: links the whole static collector and exports its `GC_*` symbols, so the shared library uses the program's collector. diff --git a/docs/memory-models.md b/docs/memory-models.md index 9849e22f3..29926ffaa 100644 --- a/docs/memory-models.md +++ b/docs/memory-models.md @@ -150,7 +150,8 @@ What to ship with a Windows program that loads a tslang shared library, all in o - the program and its shared libraries, - `gc.dll` (from `gcdll/` in the release package), -- `TypeScriptDefaultLib.dll` (from `defaultlib/dll//gc`), which every shared library +- `TypeScriptDefaultLib.dll` (from `defaultlib/dll/x86_64/pc/windows/msvc//gc`; + `tslang --print-default-lib-dir=dll [--di]` names it), which every shared library built with the default library imports. A shared library that was linked against the static `gc.lib` anyway — by an older `tslang`, or by diff --git a/tslang/build_tslang_defaultlib_debug.bat b/tslang/build_tslang_defaultlib_debug.bat index 4f66ccd72..1452d4b84 100644 --- a/tslang/build_tslang_defaultlib_debug.bat +++ b/tslang/build_tslang_defaultlib_debug.bat @@ -2,8 +2,8 @@ pushd cd ../../TypeScriptCompilerDefaultLib/ call build.bat -rem Copy the whole staged defaultlib tree so per-build subfolders are preserved: -rem defaultlib\dll\{debug,release}\{gc,rc,none}, defaultlib\lib\{debug,release}\{gc,rc,none}, +rem Copy the whole staged defaultlib tree so every target's, build's and model's subfolders are +rem preserved: defaultlib\{dll,lib}\\\\\{debug,release}\{gc,rc,none}, rem *.d.ts, generics\ xcopy __build\defaultlib\*.* "../TypeScriptCompiler/__build/tslang/windows-msbuild-2026-debug/bin/defaultlib/" /i /e /y diff --git a/tslang/build_tslang_defaultlib_release.bat b/tslang/build_tslang_defaultlib_release.bat index 80db6aeef..08d1d5655 100644 --- a/tslang/build_tslang_defaultlib_release.bat +++ b/tslang/build_tslang_defaultlib_release.bat @@ -2,8 +2,8 @@ pushd cd ../../TypeScriptCompilerDefaultLib/ call build.bat -rem Copy the whole staged defaultlib tree so per-build subfolders are preserved: -rem defaultlib\dll\{debug,release}\{gc,rc,none}, defaultlib\lib\{debug,release}\{gc,rc,none}, +rem Copy the whole staged defaultlib tree so every target's, build's and model's subfolders are +rem preserved: defaultlib\{dll,lib}\\\\\{debug,release}\{gc,rc,none}, rem *.d.ts, generics\ xcopy __build\defaultlib\*.* "../TypeScriptCompiler/__build/tslang/windows-msbuild-2026-release/bin/defaultlib/" /i /e /y diff --git a/tslang/include/TypeScript/Defines.h b/tslang/include/TypeScript/Defines.h index 87e4ad0bf..885cf9d56 100644 --- a/tslang/include/TypeScript/Defines.h +++ b/tslang/include/TypeScript/Defines.h @@ -318,27 +318,23 @@ // warns about, and it is the largest instance of it, since every program that does not pass // `--no-default-lib` links this one. // -// Layout: defaultlib/{lib,dll}/[x86/]{debug,release}/{gc,rc,none}/. The model name is exactly -// `memoryModelName()`, so the directory and the `-mm=` flag cannot drift apart. +// The model name is exactly `memoryModelName()`, so the directory and the `-mm=` flag cannot +// drift apart. #define DEFAULT_LIB_KIND_STATIC "lib" #define DEFAULT_LIB_KIND_SHARED "dll" -// ...and, for a Windows x86 target, per arch, outermost under lib/ or dll/ like every other x86 -// library (spec "Arch in the library layout"). Every other target, x64 included, has no arch -// segment, so existing trees keep their shape. There is no fallback to the x64 tree: an x64 -// library links into an x86 program only to fail on every symbol. -#define DEFAULT_LIB_ARCH_X86 "x86" - -inline std::string getDefaultLibSubDir(bool shared, bool debugBuild, const char *memoryModel, const char *arch) -{ - std::string subDir = std::string(DEFAULT_LIB_DIR "/") + (shared ? DEFAULT_LIB_KIND_SHARED : DEFAULT_LIB_KIND_STATIC) + "/"; - if (arch && *arch) - { - subDir += std::string(arch) + "/"; - } - - return subDir + (debugBuild ? DEFAULT_LIB_BUILD_DIR_DEBUG : DEFAULT_LIB_BUILD_DIR_RELEASE) + "/" + memoryModel; -} +// ...and, outermost under lib/ or dll/, per target, as the LLVM triple names it: arch, vendor, OS +// and environment, each the canonical name LLVM gives the parsed component, so the spellings of +// one target (i686/i386, an Android API level, an MSVC version) share a tree. Every target is +// keyed this way, the host included. There is no fallback to another target's tree: a library +// built for one links into a program for another only to fail on every symbol. +// +// Layout: defaultlib/{lib,dll}/////{debug,release}/{gc,rc,none}/, e.g. +// defaultlib/lib/x86_64/pc/windows/msvc/release/gc or defaultlib/lib/aarch64/unknown/linux/android/ +// release/gc. Everything else (*.d.ts, generics/, ...) stays at the defaultlib root, shared by +// every build. Composed in ONE place, getDefaultLibSubDir (tslang/tslang/defaultlib.cpp), which +// the linker, the JIT, --install-default-lib and --print-default-lib-dir all call; the build +// scripts ask --print-default-lib-dir rather than spelling the path out. #define DEBUG_SCOPE "current" #define CU_DEBUG_SCOPE "compileUnit" diff --git a/tslang/include/TypeScript/VSCodeTemplate/Files.h b/tslang/include/TypeScript/VSCodeTemplate/Files.h index 6a1122741..48199bbfb 100644 --- a/tslang/include/TypeScript/VSCodeTemplate/Files.h +++ b/tslang/include/TypeScript/VSCodeTemplate/Files.h @@ -301,21 +301,27 @@ project(<> CXX TSLANG) # Include folders include_directories(${CMAKE_TSLANG_DIR}/defaultlib) -# The compiled default lib is split into per-build subfolders (debug/release) and then per -# memory model (gc/rc/none); pick the pair matching this build, so that the CRT, the allocator -# and the default-lib binaries all agree. A library built for one model cannot be linked into -# a program built for another. +# The compiled default lib is split per target, then per build (debug/release), then per memory +# model (gc/rc/none); pick the one matching this build, so that the CRT, the allocator and the +# default-lib binaries all agree. A library built for one model cannot be linked into a program +# built for another. tslang names the folder (--print-default-lib-dir), so this never spells out +# the layout. if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(TSLANG_DEFAULTLIB_BUILD "release") + set(TSLANG_DEFAULTLIB_BUILD_FLAG "") else() - set(TSLANG_DEFAULTLIB_BUILD "debug") + set(TSLANG_DEFAULTLIB_BUILD_FLAG "--di") endif() set(TSLANG_MEMORY_MODEL "gc" CACHE STRING "Memory model of compiled code: gc, rc or none") set_property(CACHE TSLANG_MEMORY_MODEL PROPERTY STRINGS gc rc none) +execute_process(COMMAND "${CMAKE_TSLANG_COMPILER}" --print-default-lib-dir=lib ${TSLANG_DEFAULTLIB_BUILD_FLAG} -mm=${TSLANG_MEMORY_MODEL} + OUTPUT_VARIABLE TSLANG_DEFAULTLIB_SUBDIR + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Lib folders -link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/defaultlib/lib/${TSLANG_DEFAULTLIB_BUILD}/${TSLANG_MEMORY_MODEL}) +link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/${TSLANG_DEFAULTLIB_SUBDIR}) # set options if (CMAKE_BUILD_TYPE STREQUAL "Release") @@ -480,7 +486,8 @@ the build matching the model it was compiled with. One variable drives both: cmake --preset default -DTSLANG_MEMORY_MODEL=rc ``` -It selects `defaultlib/lib//` as the link directory and adds `-mm=` +It selects `defaultlib/lib//////` (as +`tslang --print-default-lib-dir=lib` names it) as the link directory and adds `-mm=` to the compile flags, so the two cannot disagree. Valid values are `gc` (default), `rc` and `none`; only `gc` links Boehm. diff --git a/tslang/test/check-x86-libs.sh b/tslang/test/check-x86-libs.sh index d6223522d..6764eacc4 100755 --- a/tslang/test/check-x86-libs.sh +++ b/tslang/test/check-x86-libs.sh @@ -23,7 +23,12 @@ RT_X86="$REPO/__build/tslang-runtime/release/x86/TypeScriptAsyncRuntime.lib" # library); only the x64-control case is SKIPPED. There is no x86 default library staged here - # the x86 default library (spec Phase 4, As built) has its own run coverage in check-x86-run.sh's # default-library section - so this file has no positive x86 case. -DEFAULTLIB_X64_LIB="$REPO/../TypeScriptCompilerDefaultLib/__build/defaultlib/lib/release/gc/TypeScriptDefaultLib.lib" +X86="i686-pc-windows-msvc" +X64="x86_64-pc-windows-msvc" +# the default library's folders under --default-lib-path, from tslang (getDefaultLibSubDir) +DL_X64_SUBDIR="$("$TSLANG" --print-default-lib-dir=lib -mm=gc -mtriple="$X64")" +DL_X86_SUBDIR="$("$TSLANG" --print-default-lib-dir=lib -mm=gc -mtriple="$X86")" +DEFAULTLIB_X64_LIB="$REPO/../TypeScriptCompilerDefaultLib/__build/$DL_X64_SUBDIR/TypeScriptDefaultLib.lib" for f in "$GC_X64" "$GC_X86" "$GCDLL_X86_LIB" "$GCDLL_X86_DLL" "$GCDLL_X64_DLL" "$RT_X64" "$RT_X86"; do if [ ! -f "$f" ]; then @@ -42,8 +47,8 @@ mkdir -p "$work/empty" \ "$work/gcdll-good/x86" "$work/gcdll-x64-dll/x86" \ "$work/rt-good/x86" "$work/rt-x64-in-x86/x86" \ "$work/gcdll-x86-flat" "$work/rt-x86-flat" \ - "$work/defaultlib-x64-only/defaultlib/lib/release/gc" \ - "$work/defaultlib-x64-in-x86/defaultlib/lib/x86/release/gc" + "$work/defaultlib-x64-only/$DL_X64_SUBDIR" \ + "$work/defaultlib-x64-in-x86/$DL_X86_SUBDIR" cp "$GC_X86" "$work/gc-good/x86/gc.lib" cp "$GC_X64" "$work/gc-x64-in-x86/x86/gc.lib" cp "$GC_X86" "$work/gc-x86-flat/gc.lib" @@ -56,17 +61,18 @@ cp "$RT_X64" "$work/rt-x64-in-x86/x86/TypeScriptAsyncRuntime.lib" cp "$GCDLL_X86_LIB" "$work/gcdll-x86-flat/gc.lib" cp "$GCDLL_X86_DLL" "$work/gcdll-x86-flat/gc.dll" cp "$RT_X86" "$work/rt-x86-flat/TypeScriptAsyncRuntime.lib" -# defaultlib-x64-only/defaultlib/lib/release/gc: a stand-in file. The "no x86 tree" case below +# defaultlib-x64-only/: a stand-in file. The "no x86 tree" case below # never opens it - it fails on the missing x86 subdirectory before reading any file - and the # "x64 control" case only runs against this tree, with the real x64 default library copied over # it, when one is staged (see below). defaultlib-x64-in-x86 needs only *some* x64 COFF archive: # the machine check reads the file's COFF header before ever trying to link it as a default # library, so $GC_X64 stands in without this file depending on a TypeScriptCompilerDefaultLib build. -cp "$GC_X64" "$work/defaultlib-x64-only/defaultlib/lib/release/gc/TypeScriptDefaultLib.lib" -cp "$GC_X64" "$work/defaultlib-x64-in-x86/defaultlib/lib/x86/release/gc/TypeScriptDefaultLib.lib" - -X86="i686-pc-windows-msvc" -X64="x86_64-pc-windows-msvc" +cp "$GC_X64" "$work/defaultlib-x64-only/$DL_X64_SUBDIR/TypeScriptDefaultLib.lib" +cp "$GC_X64" "$work/defaultlib-x64-in-x86/$DL_X86_SUBDIR/TypeScriptDefaultLib.lib" +# A default-library tree's declarations: tslang reads defaultlib/lib.d.ts before it links and, +# since #371, a missing one is an error of its own that would mask the cases below. +: > "$work/defaultlib-x64-only/defaultlib/lib.d.ts" +: > "$work/defaultlib-x64-in-x86/defaultlib/lib.d.ts" fail=0 # expect_error ... -- ... @@ -202,7 +208,7 @@ expect_no_flat_error "dll: correct x86 layout" dll "--gc-shared-lib-path=$work/g # library is staged for this script to find); check-x86-run.sh's default-library section adds run # coverage once one is built. expect_default_lib_error "default lib: no x86 tree" "$X86" "$work/defaultlib-x64-only" \ - "no x86 default library built for -mm=gc" "defaultlib" "x86" "release" "gc" -- \ + "no default library built for $X86 -mm=gc" "i386" "msvc" "release" "gc" -- \ "$GOOD_GC" "$GOOD_RT" expect_default_lib_error "default lib: x64 lib in x86 tree" "$X86" "$work/defaultlib-x64-in-x86" \ "is built for x64" -- \ @@ -211,7 +217,7 @@ expect_default_lib_error "default lib: x64 lib in x86 tree" "$X86" "$work/defaul # build); the two error cases above are satisfied by the stand-in files staged above. SKIPPED, not # FAILed, when none is staged, matching check-x86-run.sh's skip style for the same situation. if [ -f "$DEFAULTLIB_X64_LIB" ]; then - cp "$DEFAULTLIB_X64_LIB" "$work/defaultlib-x64-only/defaultlib/lib/release/gc/TypeScriptDefaultLib.lib" + cp "$DEFAULTLIB_X64_LIB" "$work/defaultlib-x64-only/$DL_X64_SUBDIR/TypeScriptDefaultLib.lib" expect_default_lib_ok "default lib: x64 control" "$X64" "$work/defaultlib-x64-only" \ "--gc-lib-path=$(dirname "$GC_X64")" "--tslang-lib-path=$(dirname "$RT_X64")" else diff --git a/tslang/test/check-x86-run.sh b/tslang/test/check-x86-run.sh index 2a07e76a0..f5cc14c76 100755 --- a/tslang/test/check-x86-run.sh +++ b/tslang/test/check-x86-run.sh @@ -48,6 +48,8 @@ GCDLL_DIR="$REPO/3rdParty/gcdll/x64/release/lib" # see its build.bat). DEFAULT_LIB_PATH is unset above for hermeticity, so this is the one candidate # defaultlib-collector.cmake's DEFAULT_LIB_CANDIDATES pair resolves to from this repo's location. DEFAULTLIB_DIR="$REPO/../TypeScriptCompilerDefaultLib/__build" +# its x86 release gc static library's folder, from tslang (getDefaultLibSubDir) +DEFAULTLIB_X86_SUBDIR="$("$TSLANG" --print-default-lib-dir=lib -mm=gc -mtriple=i686-pc-windows-msvc)" DEFAULTLIB_TESTS="$REPO/tslang/test/tester/defaultlib" # The async corpus cases (gc only), from test/tester/tests. They are built with --entry-point to @@ -259,10 +261,9 @@ fi # --- The x86 default library (spec Phase 4, As built) --------------------------------------- # Skipped, not failed, the same rule defaultlib-collector.cmake applies for the x64 collector # tests: this is a build product from a sibling repository, not something this script -# can build. `defaultlib/lib/x86` is the layout Defines.h's getDefaultLibSubDir names for any -# model/build, so its presence (regardless of model) is the gate. -if [ ! -d "$DEFAULTLIB_DIR/defaultlib/lib/x86" ]; then - echo "SKIPPED: no x86 default library built at $DEFAULTLIB_DIR/defaultlib/lib/x86" \ +# can build. Its release gc tree, which every build of it produces, is the gate. +if [ ! -d "$DEFAULTLIB_DIR/$DEFAULTLIB_X86_SUBDIR" ]; then + echo "SKIPPED: no x86 default library built at $DEFAULTLIB_DIR/$DEFAULTLIB_X86_SUBDIR" \ "(build it in TypeScriptCompilerDefaultLib with TSLANG_ARCH=x86; see its build.bat)" else DL_FLAG="--default-lib-path=$DEFAULTLIB_DIR" @@ -325,7 +326,7 @@ else echo "FAIL defaultlib dll: library compile failed (exit $status): $err" fail=1 else - cp "$DEFAULTLIB_DIR/defaultlib/dll/x86/release/gc/TypeScriptDefaultLib.dll" "$dll_dir/" + cp "$DEFAULTLIB_DIR/$("$TSLANG" --print-default-lib-dir=dll -mm=gc -mtriple=i686-pc-windows-msvc)/TypeScriptDefaultLib.dll" "$dll_dir/" cp "$GCDLL_DIR/x86/gc.dll" "$dll_dir/" err="$(cd "$dll_dir" && "$TSLANG" --emit=exe --opt -mm=gc -mtriple=i686-pc-windows-msvc \ "$DL_FLAG" "$GC_FLAG" "$RT_FLAG" "--gc-shared-lib-path=$GCDLL_DIR" \ diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index e32b56e78..7b0392f13 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2437,6 +2437,12 @@ add_test(NAME test-compile-target-crt "-DWORK_DIR=${CMAKE_CURRENT_BINARY_DIR}/target-crt" -P "${CMAKE_CURRENT_SOURCE_DIR}/target-crt.cmake") +# The default library's folder per target, build and model - see default-lib-dir.cmake. +add_test(NAME test-default-lib-dir + COMMAND ${CMAKE_COMMAND} + "-DTSLANG=$" + -P "${CMAKE_CURRENT_SOURCE_DIR}/default-lib-dir.cmake") + # Compiling and linking for Android - see android-link.cmake. `codegen` needs no NDK and always # runs; `link` is skipped (not failed) without the NDK and the Android libraries. foreach(android_mode codegen link) @@ -2521,7 +2527,6 @@ if (TSLANG_TEST_X86) add_test(NAME test-x86-compile-gc-defaultlib-collector COMMAND ${CMAKE_COMMAND} "-DMODE=compile" - "-DX86=1" "-DTSLANG=$" "-DTSLANG_BIN=${CMAKE_BINARY_DIR}/bin" "-DTESTS_DIR=${CMAKE_CURRENT_SOURCE_DIR}/defaultlib" diff --git a/tslang/test/tester/android-link.cmake b/tslang/test/tester/android-link.cmake index 1e7c448de..abcee70c2 100644 --- a/tslang/test/tester/android-link.cmake +++ b/tslang/test/tester/android-link.cmake @@ -9,8 +9,8 @@ # library, collector and async runtime, through the NDK. Prints SKIPPED (CTest then # reports it skipped) unless ANDROID_NDK_HOME and the Android builds of those are # there: scripts/build_gc_release_android.bat, scripts/build_tslang_runtime_release_ -# android.bat, and TypeScriptCompilerDefaultLib's scripts/build_android.bat (found -# next to this repository, or at TSLANG_ANDROID_DEFAULT_LIB_PATH). A shared library +# android.bat, and TypeScriptCompilerDefaultLib's scripts/build_android.bat (its +# __build next to this repository, or TSLANG_ANDROID_DEFAULT_LIB_PATH). A shared library # links with --no-undefined, so a symbol Bionic lacks fails here, not in the app. cmake_minimum_required(VERSION 3.17.3) @@ -97,15 +97,20 @@ elseif(MODE STREQUAL "link") if(DEFINED ENV{TSLANG_ANDROID_DEFAULT_LIB_PATH}) set(default_lib "$ENV{TSLANG_ANDROID_DEFAULT_LIB_PATH}") else() - set(default_lib "${REPO_DIR}/../TypeScriptCompilerDefaultLib/__build/android/${abi}") + set(default_lib "${REPO_DIR}/../TypeScriptCompilerDefaultLib/__build") endif() + # the one default-library tree, every target in it; tslang names this one's folder + execute_process(COMMAND "${TSLANG}" --print-default-lib-dir=lib -mm=gc -mtriple=${triple} + OUTPUT_VARIABLE default_lib_subdir + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) if("$ENV{ANDROID_NDK_HOME}" STREQUAL "") message("SKIPPED: ANDROID_NDK_HOME is not set") return() endif() foreach(file "${gc_lib}/libgc.a" "${runtime_lib}/libTypeScriptAsyncRuntime.a" - "${default_lib}/defaultlib/lib/release/gc/libTypeScriptDefaultLib.a") + "${default_lib}/${default_lib_subdir}/libTypeScriptDefaultLib.a") if(NOT EXISTS "${file}") message("SKIPPED: ${file} is not built") return() diff --git a/tslang/test/tester/default-lib-dir.cmake b/tslang/test/tester/default-lib-dir.cmake new file mode 100644 index 000000000..8617b2733 --- /dev/null +++ b/tslang/test/tester/default-lib-dir.cmake @@ -0,0 +1,34 @@ +# The default library's layout, as tslang --print-default-lib-dir names it: the build scripts of +# TypeScriptCompilerDefaultLib stage into what it prints and tslang links from the same function +# (getDefaultLibSubDir), so a change to the layout shows up here, as a changed literal, before a +# default library built one way is looked for the other way. + +cmake_minimum_required(VERSION 3.17.3) + +if(NOT DEFINED TSLANG) + message(FATAL_ERROR "TSLANG is required") +endif() + +# check( ) +function(check expected) + execute_process(COMMAND "${TSLANG}" ${ARGN} + OUTPUT_VARIABLE out + ERROR_VARIABLE err + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE status) + if(NOT status EQUAL 0 OR NOT out STREQUAL "${expected}") + message(FATAL_ERROR "tslang ${ARGN}: exit ${status}\nexpected: ${expected}\n got: ${out}\n${err}") + endif() +endfunction() + +check("defaultlib/lib/x86_64/pc/windows/msvc/release/gc" --print-default-lib-dir=lib -mtriple=x86_64-pc-windows-msvc) +check("defaultlib/dll/x86_64/pc/windows/msvc/debug/rc" --print-default-lib-dir=dll --di -mm=rc -mtriple=x86_64-pc-windows-msvc) +check("defaultlib/lib/x86_64/unknown/linux/gnu/release/none" --print-default-lib-dir=lib -mm=none -mtriple=x86_64-unknown-linux-gnu) +check("defaultlib/lib/aarch64/unknown/linux/android/release/gc" --print-default-lib-dir=lib -mtriple=aarch64-linux-android29) + +# the spellings of one target share its tree: the canonical arch name, no API level or version +check("defaultlib/lib/i386/pc/windows/msvc/release/gc" --print-default-lib-dir=lib -mtriple=i686-pc-windows-msvc) +check("defaultlib/lib/i386/pc/windows/msvc/release/gc" --print-default-lib-dir=lib -mtriple=i386-pc-windows-msvc19.40.0) +check("defaultlib/lib/x86_64/unknown/linux/android/release/gc" --print-default-lib-dir=lib -mtriple=x86_64-linux-android34) + +message(STATUS "--print-default-lib-dir names the triple/build/model layout") diff --git a/tslang/test/tester/defaultlib-collector.cmake b/tslang/test/tester/defaultlib-collector.cmake index 48cf949a5..a1583f337 100644 --- a/tslang/test/tester/defaultlib-collector.cmake +++ b/tslang/test/tester/defaultlib-collector.cmake @@ -40,20 +40,20 @@ else() set(build "release") endif() -# X86 (optional; unset/OFF means the ordinary x64 tree): the i686 twin passes -DX86=1 and its own -# gc/runtime/OPT (-mtriple), and asks for the x86 arch segment the x86 default library (spec Phase 4, -# As built) gave the default-library layout (defaultlib/{lib,dll}/x86/{debug,release}/{gc,rc,none}; -# see Defines.h getDefaultLibSubDir). -if(DEFINED X86 AND X86) - set(default_lib_arch "x86/") -else() - set(default_lib_arch "") +# The folder of the shared default library for this target (OPT carries the i686 twin's -mtriple), +# build and model, from tslang itself: the layout is composed in one place, getDefaultLibSubDir. +execute_process(COMMAND "${TSLANG}" --print-default-lib-dir=dll ${OPT} -mm=gc + OUTPUT_VARIABLE default_lib_dll_subdir + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE status) +if(NOT status EQUAL 0 OR default_lib_dll_subdir STREQUAL "") + message(FATAL_ERROR "tslang --print-default-lib-dir=dll ${OPT} -mm=gc: exit ${status}") endif() # DEFAULT_LIB_PATH at run time wins, so a workflow can point at the library it just built set(default_lib "") foreach(candidate "$ENV{DEFAULT_LIB_PATH}" ${DEFAULT_LIB_CANDIDATES}) - if(NOT candidate STREQUAL "" AND IS_DIRECTORY "${candidate}/defaultlib/dll/${default_lib_arch}${build}/gc") + if(NOT candidate STREQUAL "" AND IS_DIRECTORY "${candidate}/${default_lib_dll_subdir}") set(default_lib "${candidate}") break() endif() @@ -62,13 +62,13 @@ endforeach() if(default_lib STREQUAL "") # a workflow that has just built the library sets this, so a wrong path fails instead of skipping if("$ENV{TSLANG_REQUIRE_DEFAULT_LIB}" STREQUAL "1") - message(FATAL_ERROR "no ${default_lib_arch}default library built for ${build}/gc under DEFAULT_LIB_PATH='$ENV{DEFAULT_LIB_PATH}'") + message(FATAL_ERROR "no default library built in ${default_lib_dll_subdir} under DEFAULT_LIB_PATH='$ENV{DEFAULT_LIB_PATH}'") endif() - message("SKIPPED: no ${default_lib_arch}default library built for ${build}/gc (set DEFAULT_LIB_PATH to the folder holding 'defaultlib')") + message("SKIPPED: no default library built in ${default_lib_dll_subdir} (set DEFAULT_LIB_PATH to the folder holding 'defaultlib')") return() endif() -set(default_lib_dll_dir "${default_lib}/defaultlib/dll/${default_lib_arch}${build}/gc") +set(default_lib_dll_dir "${default_lib}/${default_lib_dll_subdir}") message(STATUS "default library: ${default_lib}") set(ENV{GC_SHARED_LIB_PATH} "") diff --git a/tslang/tslang/defaultlib.cpp b/tslang/tslang/defaultlib.cpp index e30e0f820..dfdc8bb7e 100644 --- a/tslang/tslang/defaultlib.cpp +++ b/tslang/tslang/defaultlib.cpp @@ -14,6 +14,8 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/Program.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/Triple.h" #include "TypeScript/TypeScriptCompiler/Defines.h" #include "TypeScript/Defines.h" @@ -53,6 +55,24 @@ std::string getDefaultLibPath(); std::string getpath(std::string, const SmallVectorImpl&); std::error_code copy_from_to(const SmallVectorImpl&, const SmallVectorImpl&); +// The subfolder of --default-lib-path holding the default library's binaries for a target, a build +// mode and a memory model; the layout and why it is keyed this way are in Defines.h. The ONE place +// the path is composed: never spell it out elsewhere, ask this (or --print-default-lib-dir). +std::string getDefaultLibSubDir(bool shared, bool debugBuild, const char *memoryModel, const llvm::Triple &triple) +{ + std::string subDir = std::string(DEFAULT_LIB_DIR "/") + (shared ? DEFAULT_LIB_KIND_SHARED : DEFAULT_LIB_KIND_STATIC); + // the canonical component names, not the triple as typed: i686 and i386 are one arch, and the + // API level of an Android triple (android29) or the version of an MSVC one is not a target + for (auto segment : {llvm::Triple::getArchTypeName(triple.getArch()), llvm::Triple::getVendorTypeName(triple.getVendor()), + llvm::Triple::getOSTypeName(triple.getOS()), llvm::Triple::getEnvironmentTypeName(triple.getEnvironment())}) + { + subDir += "/"; + subDir += segment; + } + + return subDir + "/" + (debugBuild ? DEFAULT_LIB_BUILD_DIR_DEBUG : DEFAULT_LIB_BUILD_DIR_RELEASE) + "/" + memoryModel; +} + bool checkFileExistsAtPath(const SmallVectorImpl& path, std::string subPath, std::string fileName) { llvm::SmallVector destPath(0); @@ -140,11 +160,11 @@ int installDefaultLib(int argc, char **argv) // The release build of the default memory model is always produced; verify its static lib // landed in the subfolder the compiler will later look in. Built from getDefaultLibSubDir // rather than spelled out here, so this check cannot drift away from what exe.cpp and - // jit.cpp resolve (defaultlib/lib/release/gc/...). + // jit.cpp resolve (defaultlib/lib//release/gc/...). auto result = checkFileExistsAtPath( builtPath, getDefaultLibSubDir(/*shared=*/false, /*debugBuild=*/false, memoryModelName(MemoryModelGC), - /*arch=*/""), // install checks the host build + llvm::Triple(llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))), // install builds for the host #ifdef WIN32 DEFAULT_LIB_NAME ".lib" #else diff --git a/tslang/tslang/exe.cpp b/tslang/tslang/exe.cpp index f05f856d9..58a37a9ae 100644 --- a/tslang/tslang/exe.cpp +++ b/tslang/tslang/exe.cpp @@ -56,6 +56,7 @@ extern cl::opt verbose; std::string getDefaultOutputFileName(enum Action); std::string mergeWithDefaultLibPath(std::string, std::string); +std::string getDefaultLibSubDir(bool, bool, const char *, const llvm::Triple &); using llvm::StringRef; @@ -689,9 +690,6 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio auto shared = emitAction == BuildDll; // Windows x86 and x64 link libraries whose machine tslang checks; see resolveWindowsLibPath. auto winX86OrX64 = win && (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64); - // The default library keeps a separate x86 tree, the same rule getTargetLibDir applies to - // the collector and the runtime; see getDefaultLibSubDir. - auto x86DefaultLib = win && arch == llvm::Triple::x86; // the same debug/release choice as the CRT below std::string libConfig = enableOpt ? "release" : "debug"; @@ -803,14 +801,15 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio // Keyed on --di (generate debug info): with debug info use the debug lib. // ...and per memory model: the default lib allocates the way the model it was built for // allocates, so a `gc` build linked into an `-mm=rc` program would drag Boehm in and hand - // back objects this program's ownership rules do not describe. See getDefaultLibSubDir. + // back objects this program's ownership rules do not describe. ...and per target triple: + // a library built for one target fails on every symbol in a program for another. See + // getDefaultLibSubDir. // Android: a shared library links the static default library too, so the app ships one // self-contained .so. Its shared default library leaves GC_* to the process, and an app's // process (the Java VM) has no collector to give it. auto sharedDefaultLib = shared && !android; auto defaultLibSubDir = getDefaultLibSubDir(sharedDefaultLib, compileOptions.generateDebugInfo, - memoryModelName(compileOptions.memoryModel), - x86DefaultLib ? DEFAULT_LIB_ARCH_X86 : ""); + memoryModelName(compileOptions.memoryModel), TheTriple); auto defaultLibDir = mergeWithDefaultLibPath(getDefaultLibPath(), defaultLibSubDir); // Checked here rather than left to the linker. mergeWithDefaultLibPath only joins the @@ -821,11 +820,9 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio // far harder to diagnose than a directory that is not there. if (!defaultLibDir.empty() && !llvm::sys::fs::is_directory(defaultLibDir)) { - llvm::errs() << "error: no " << (x86DefaultLib ? "x86 " : "") << "default library built for -mm=" + llvm::errs() << "error: no default library built for " << TheTriple.str() << " -mm=" << memoryModelName(compileOptions.memoryModel) << ": " << defaultLibDir - << " does not exist. " - << (x86DefaultLib ? "Build it in TypeScriptCompilerDefaultLib with TSLANG_ARCH=x86 (see its build.bat), " - : "Build it (see the default-lib build scripts), ") + << " does not exist. Build it (see the default-lib build scripts), " << "or compile with --no-default-lib.\n"; return 1; } diff --git a/tslang/tslang/jit.cpp b/tslang/tslang/jit.cpp index 9c60742ec..a69df5620 100644 --- a/tslang/tslang/jit.cpp +++ b/tslang/tslang/jit.cpp @@ -69,6 +69,7 @@ extern cl::opt TargetTriple; std::string getDefaultLibPath(); std::string getTslangLibPath(); std::string mergeWithDefaultLibPath(std::string, std::string); +std::string getDefaultLibSubDir(bool, bool, const char *, const llvm::Triple &); std::string makeAbsolutePath(std::string); int registerMLIRDialects(mlir::ModuleOp); @@ -596,7 +597,8 @@ static int prepareJitProcess(CompileOptions &compileOptions) // allocates the way the model it was built for allocates. See getDefaultLibSubDir. auto defaultLibSubDir = getDefaultLibSubDir(/*shared=*/true, compileOptions.generateDebugInfo, - memoryModelName(compileOptions.memoryModel), /*arch=*/""); // JIT is host-only + memoryModelName(compileOptions.memoryModel), + llvm::Triple(llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))); // JIT is host-only auto defaultLibFile = mergeWithDefaultLibPath(getDefaultLibPath(), #ifdef WIN32 defaultLibSubDir + "/" DEFAULT_LIB_NAME ".dll" diff --git a/tslang/tslang/tslang.cpp b/tslang/tslang/tslang.cpp index 5a935370f..8cde6dc9f 100644 --- a/tslang/tslang/tslang.cpp +++ b/tslang/tslang/tslang.cpp @@ -60,6 +60,7 @@ int runMLIRPasses(mlir::MLIRContext &, llvm::SourceMgr &, mlir::OwningOpRef TargetTriple; cl::OptionCategory TypeScriptCompilerCategory("Compiler Options"); cl::OptionCategory TypeScriptCompilerDebugCategory("JIT Debug Options"); cl::OptionCategory TypeScriptCompilerBuildCategory("Executable/Shared library Build Options(used in -emit=exe and -emit=dll)"); @@ -168,6 +170,19 @@ cl::opt newVSCodeFolder("new", cl::desc("New VS Code Project"), cl::cat(Ty cl::opt newCMakeFolder("cmake", cl::desc("New CMake Project"), cl::cat(TypeScriptCompilerCategory)); cl::opt installDefaultLibCmd("install-default-lib", cl::desc("Install Default Library. use default-lib-path to provide path where to install the lib"), cl::cat(TypeScriptCompilerCategory)); +// What a build script stages into and a CMake project links from, so neither has to spell out the +// layout (see getDefaultLibSubDir): the subfolder of --default-lib-path for -mtriple, --di and -mm. +enum DefaultLibKind +{ + DefaultLibKindNotAsked, + DefaultLibKindStatic, + DefaultLibKindShared +}; +cl::opt printDefaultLibDir("print-default-lib-dir", cl::desc("Print the folder, relative to --default-lib-path, holding the default library built for -mtriple (default: the host), --di (debug, else release) and -mm, then exit"), + cl::values(clEnumValN(DefaultLibKindStatic, "lib", "the static library")), + cl::values(clEnumValN(DefaultLibKindShared, "dll", "the shared library")), + cl::init(DefaultLibKindNotAsked), cl::cat(TypeScriptCompilerBuildCategory)); + static void TslangPrintVersion(llvm::raw_ostream &OS) { OS << "TypeScript Compiler (https://github.com/ASDAlexander77/TypeScriptCompiler):" << '\n'; @@ -422,6 +437,14 @@ int main(int argc, char **argv) enableOpt.setValue(true); } + if (printDefaultLibDir != DefaultLibKindNotAsked) + { + auto triple = llvm::Triple(llvm::Triple::normalize(TargetTriple.empty() ? llvm::sys::getDefaultTargetTriple() : TargetTriple.getValue())); + llvm::outs() << getDefaultLibSubDir(printDefaultLibDir == DefaultLibKindShared, generateDebugInfo, + memoryModelName(memoryModelOpt), triple) << "\n"; + return 0; + } + if (newVSCodeFolder.getValue()) { return createVSCodeFolder(argc, argv);