diff --git a/docs/how/cmake_dll/CMakeLists.txt b/docs/how/cmake_dll/CMakeLists.txt new file mode 100644 index 000000000..72b88a6ff --- /dev/null +++ b/docs/how/cmake_dll/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.20) + +# Make CMake find ts-language modules +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +project(anax_enhancer CXX) + +# Enable TS-language +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. +if (CMAKE_BUILD_TYPE STREQUAL "Release") + set(TSLANG_DEFAULTLIB_BUILD "release") +else() + set(TSLANG_DEFAULTLIB_BUILD "debug") +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) + +# Lib folders +link_directories(${CMAKE_TSLANG_DIR} ${CMAKE_TSLANG_DIR}/defaultlib/lib/${TSLANG_DEFAULTLIB_BUILD}/${TSLANG_MEMORY_MODEL}) + +# set options +if (CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_TSLANG_FLAGS "--opt --opt_level=3") # global +else() + set(CMAKE_TSLANG_FLAGS "--di --opt_level=0") # global +endif() + +# The same variable that picked the link directory has to reach the compiler as well, or the +# program is compiled for one model and linked against another model's default lib. +set(CMAKE_TSLANG_FLAGS "${CMAKE_TSLANG_FLAGS} -mm=${TSLANG_MEMORY_MODEL}") # global + +if(WIN32) +else() + set(CMAKE_TSLANG_FLAGS "${CMAKE_TSLANG_FLAGS} -relocation-model=pic") # global +endif() + +# .ts files compile with TSLANG command; .cpp with the C++ compiler. +add_library("opengl32" SHARED + dummy.cpp + wrapper.ts +) + +# required libs +set(TSLANG_LINK_LIBS "TypeScriptDefaultLib" "TypeScriptAsyncRuntime" "LLVMSupport") + +# Boehm is only referenced by the gc default lib; the rc and none builds allocate through the +# CRT and must not drag a collector in. +if (TSLANG_MEMORY_MODEL STREQUAL "gc") + list(APPEND TSLANG_LINK_LIBS "gcdll/gc") +endif() + +# ntdll provides RtlGetLastNtStatus (pulled in by LLVMSupport) on Windows +if(WIN32) + list(APPEND TSLANG_LINK_LIBS "ntdll") +else() + list(APPEND TSLANG_LINK_LIBS "LLVMDemangle" "stdc++" "m" "pthread" "tinfo" "dl" "rt") +endif() + +target_link_libraries("opengl32" ${TSLANG_LINK_LIBS}) + +# The GC-model default lib depends on gc.dll at runtime; copy it next to the +# built binary so the program can find it without relying on PATH. +if (TSLANG_MEMORY_MODEL STREQUAL "gc") + add_custom_command(TARGET "opengl32" POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_TSLANG_DIR}/gcdll/gc.dll" + "$" + ) +endif() diff --git a/docs/how/cmake_dll/CMakePresets.json b/docs/how/cmake_dll/CMakePresets.json new file mode 100644 index 000000000..4a7375a79 --- /dev/null +++ b/docs/how/cmake_dll/CMakePresets.json @@ -0,0 +1,23 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default (Ninja)", + "description": "TSLANG custom language requires the Ninja generator (the Visual Studio generator ignores custom languages).", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build" + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default" + } + ] +} diff --git a/docs/how/cmake_dll/README.md b/docs/how/cmake_dll/README.md new file mode 100644 index 000000000..2c5a2abc1 --- /dev/null +++ b/docs/how/cmake_dll/README.md @@ -0,0 +1,70 @@ +# Custom language with your own extension and compile command in CMake + +This registers TypeScript CMake *language* (`TSLANG`) that: + +- owns its own source extension (`.ts`), +- is built with **tslang --emit=obj**, +- produces `.obj` files that CMake links automatically alongside regular C++. + +CMake then handles dependency tracking and incremental builds for `.ts` +sources the same way it does for `.cpp`. + +## Layout + +``` +custom_lang/ +├── CMakeLists.txt +├── cmake/ +│ ├── CMakeDetermineTSLANGCompiler.cmake +│ ├── CMakeTSLANGCompiler.cmake.in +│ ├── CMakeTSLANGInformation.cmake +│ └── CMakeTestTSLANGCompiler.cmake +├── main.cpp +└── mycode.ts +``` + +## How it works + +- `enable_language(TSLANG)` runs `CMakeDetermineTSLANGCompiler.cmake`, which finds + `tslangc`, then loads `CMakeTSLANGInformation.cmake`, which registers the compile + rule (`CMAKE_TSLANG_COMPILE_OBJECT` — *tslang --emit=obj*). +- Because `CMAKE_TSLANG_SOURCE_FILE_EXTENSIONS` contains `tslang`, any `.ts` source + is routed to your command and compiled to a `.obj`. +- That `.obj` is added to the target and linked with `main.cpp`'s object using + the C++ linker (`CMAKE_TSLANG_LINK_EXECUTABLE`). +- Change `mycode.ts` and only it recompiles. +- Compile: `cmake --preset default && cmake --build --preset default` + +## Passing flags + +```cmake +set(CMAKE_TSLANG_FLAGS "--opt_level=3") # global +set_source_files_properties(mycode.ts PROPERTIES + COMPILE_OPTIONS "--define;TSLANG=1") # per-file +``` + +## Memory model + +The default library is compiled separately for each memory model, and a program has to link +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=` +to the compile flags, so the two cannot disagree. Valid values are `gc` (default), `rc` and +`none`; only `gc` links Boehm. + +## Minimal alternative + +If you don't need a first-class language, either: + +- Mark a file as an already-built object and just link it: + `set_source_files_properties(mycode.obj PROPERTIES EXTERNAL_OBJECT TRUE GENERATED TRUE)` + and add it to `add_executable`, producing it with `add_custom_command`; or +- Compile a differently-named file *as C++*: + `set_source_files_properties(mycode.tslang PROPERTIES LANGUAGE CXX)`. + +Use the typescript-language setup below when `.ts` is a source type you +compile often and want CMake to treat as first-class. diff --git a/docs/how/cmake_dll/cmake/CMakeDetermineTSLANGCompiler.cmake b/docs/how/cmake_dll/cmake/CMakeDetermineTSLANGCompiler.cmake new file mode 100644 index 000000000..c714ee29f --- /dev/null +++ b/docs/how/cmake_dll/cmake/CMakeDetermineTSLANGCompiler.cmake @@ -0,0 +1,24 @@ +# Locate your custom compiler +find_program(CMAKE_TSLANG_COMPILER + NAMES tslang tslang.exe + HINTS "I:\\tslang" "${CMAKE_SOURCE_DIR}/tools" + DOC "TSLANG compiler") + +cmake_path(GET CMAKE_TSLANG_COMPILER PARENT_PATH CMAKE_TSLANG_DIR) + +mark_as_advanced(CMAKE_TSLANG_COMPILER) +mark_as_advanced(CMAKE_TSLANG_DIR) + +# Which source extensions belong to TSLANG, and the object suffix +set(CMAKE_TSLANG_SOURCE_FILE_EXTENSIONS ts) +if (NOT WIN32) + set(CMAKE_TSLANG_OUTPUT_EXTENSION .o) +else() + set(CMAKE_TSLANG_OUTPUT_EXTENSION .obj) # .o on Linux +endif() +set(CMAKE_TSLANG_COMPILER_ENV_VAR "TSLANG") + +# Emit the compiler-id config file CMake expects +configure_file( + ${CMAKE_CURRENT_LIST_DIR}/CMakeTSLANGCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeTSLANGCompiler.cmake @ONLY) diff --git a/docs/how/cmake_dll/cmake/CMakeTSLANGCompiler.cmake.in b/docs/how/cmake_dll/cmake/CMakeTSLANGCompiler.cmake.in new file mode 100644 index 000000000..90131f6ae --- /dev/null +++ b/docs/how/cmake_dll/cmake/CMakeTSLANGCompiler.cmake.in @@ -0,0 +1,6 @@ +set(CMAKE_TSLANG_COMPILER "@CMAKE_TSLANG_COMPILER@") +set(CMAKE_TSLANG_DIR "@CMAKE_TSLANG_DIR@") +set(CMAKE_TSLANG_SOURCE_FILE_EXTENSIONS @CMAKE_TSLANG_SOURCE_FILE_EXTENSIONS@) +set(CMAKE_TSLANG_OUTPUT_EXTENSION @CMAKE_TSLANG_OUTPUT_EXTENSION@) +set(CMAKE_TSLANG_COMPILER_LOADED 1) +set(CMAKE_TSLANG_COMPILER_WORKS TRUE) diff --git a/docs/how/cmake_dll/cmake/CMakeTSLANGInformation.cmake b/docs/how/cmake_dll/cmake/CMakeTSLANGInformation.cmake new file mode 100644 index 000000000..0e38b27ab --- /dev/null +++ b/docs/how/cmake_dll/cmake/CMakeTSLANGInformation.cmake @@ -0,0 +1,20 @@ +# The actual compile command. +# Placeholders CMake substitutes: +# the binary +# per-target flags +# input .ts +# output .obj +# optional +if(NOT CMAKE_TSLANG_COMPILE_OBJECT) + set(CMAKE_TSLANG_COMPILE_OBJECT + " --default-lib-path=${CMAKE_TSLANG_DIR} --emit=obj -o= ") +endif() + +# How CMake links TSLANG objects into an executable/library. +# Reuse the C++ linker so linking with .cpp works out of the box. +if(NOT CMAKE_TSLANG_LINK_EXECUTABLE) + set(CMAKE_TSLANG_LINK_EXECUTABLE + " -o ") +endif() + +set(CMAKE_TSLANG_INFORMATION_LOADED 1) diff --git a/docs/how/cmake_dll/cmake/CMakeTestTSLANGCompiler.cmake b/docs/how/cmake_dll/cmake/CMakeTestTSLANGCompiler.cmake new file mode 100644 index 000000000..8aca6675d --- /dev/null +++ b/docs/how/cmake_dll/cmake/CMakeTestTSLANGCompiler.cmake @@ -0,0 +1,2 @@ +# Skip the actual test compile; assume the compiler works. +set(CMAKE_TSLANG_COMPILER_WORKS TRUE) diff --git a/docs/how/cmake_dll/dummy.cpp b/docs/how/cmake_dll/dummy.cpp new file mode 100644 index 000000000..33c14ce1d --- /dev/null +++ b/docs/how/cmake_dll/dummy.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/how/cmake_dll/tsconfig.json b/docs/how/cmake_dll/tsconfig.json new file mode 100644 index 000000000..ed63250aa --- /dev/null +++ b/docs/how/cmake_dll/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "esnext", + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "lib": ["ESNext"], + "types": ["./types/tslang", "I:\\tslang/defaultlib/lib.d.ts"] + }, + "include": ["wrapper.ts"] +} \ No newline at end of file diff --git a/docs/how/cmake_dll/tslang.natvis b/docs/how/cmake_dll/tslang.natvis new file mode 100644 index 000000000..aebf30d77 --- /dev/null +++ b/docs/how/cmake_dll/tslang.natvis @@ -0,0 +1,18 @@ + + + + + + + array of {length} elements + + length + + length + data + + + + + + diff --git a/docs/how/cmake_dll/types/tslang/index.d.ts b/docs/how/cmake_dll/types/tslang/index.d.ts new file mode 100644 index 000000000..419dfc78a --- /dev/null +++ b/docs/how/cmake_dll/types/tslang/index.d.ts @@ -0,0 +1,44 @@ + +declare function print(...args: any[]) : void; +declare function assert(cond: boolean, msg?: string) : void; +declare type byte = any; +declare type short = any; +declare type ushort = any; +declare type int = any; +declare type uint = any; +declare type index = any; +declare type long = any; +declare type ulong = any; +declare type char = any; +declare type i8 = any; +declare type i16 = any; +declare type i32 = any; +declare type i64 = any; +declare type u8 = any; +declare type u16 = any; +declare type u32 = any; +declare type u64 = any; +declare type s8 = any; +declare type s16 = any; +declare type s32 = any; +declare type s64 = any; +declare type f16 = any; +declare type f32 = any; +declare type f64 = any; +declare type f128 = any; +declare type half = any; +declare type float = any; +declare type double = any; +declare type Opaque = any; + +type Ref = any +type Reference = Ref // deprecated alias of Ref + +declare function Ref(r: T): Ref; +declare function Deref(r: Ref): T; + +// deprecated aliases of Ref / Deref +declare function ReferenceOf(r: T): Ref; +declare function LoadReference(r: Ref): T; + +declare function sizeof(v?: T): index; diff --git a/docs/how/cmake_dll/wrapper.ts b/docs/how/cmake_dll/wrapper.ts new file mode 100644 index 000000000..3681e49d7 --- /dev/null +++ b/docs/how/cmake_dll/wrapper.ts @@ -0,0 +1,3 @@ +export function adder(a = 0, b = 0) { + return a + b; +} diff --git a/tslang/include/TypeScript/VSCodeTemplate/Files.h b/tslang/include/TypeScript/VSCodeTemplate/Files.h index 5d16f82e9..a83737359 100644 --- a/tslang/include/TypeScript/VSCodeTemplate/Files.h +++ b/tslang/include/TypeScript/VSCodeTemplate/Files.h @@ -560,7 +560,7 @@ const auto CMAKE_TSLANG_INFORMATION_DATA = R"raw(# The actual compile command. # optional if(NOT CMAKE_TSLANG_COMPILE_OBJECT) set(CMAKE_TSLANG_COMPILE_OBJECT - " --default-lib-path=${CMAKE_TSLANG_DIR} --emit=obj --export=none -o= ") + " --default-lib-path=${CMAKE_TSLANG_DIR} --emit=obj -o= ") endif() # How CMake links TSLANG objects into an executable/library.