Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/how/cmake_dll/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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"
"$<TARGET_FILE_DIR:opengl32>"
)
endif()
23 changes: 23 additions & 0 deletions docs/how/cmake_dll/CMakePresets.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
70 changes: 70 additions & 0 deletions docs/how/cmake_dll/README.md
Original file line number Diff line number Diff line change
@@ -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/<debug|release>/<model>` as the link directory and adds `-mm=<model>`
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.
24 changes: 24 additions & 0 deletions docs/how/cmake_dll/cmake/CMakeDetermineTSLANGCompiler.cmake
Original file line number Diff line number Diff line change
@@ -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)
6 changes: 6 additions & 0 deletions docs/how/cmake_dll/cmake/CMakeTSLANGCompiler.cmake.in
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions docs/how/cmake_dll/cmake/CMakeTSLANGInformation.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# The actual compile command.
# Placeholders CMake substitutes:
# <CMAKE_TSLANG_COMPILER> the binary
# <FLAGS> per-target flags
# <SOURCE> input .ts
# <OBJECT> output .obj
# <DEFINES> <INCLUDES> optional
if(NOT CMAKE_TSLANG_COMPILE_OBJECT)
set(CMAKE_TSLANG_COMPILE_OBJECT
"<CMAKE_TSLANG_COMPILER> <FLAGS> --default-lib-path=${CMAKE_TSLANG_DIR} --emit=obj -o=<OBJECT> <SOURCE>")
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
"<CMAKE_CXX_COMPILER> <FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
endif()

set(CMAKE_TSLANG_INFORMATION_LOADED 1)
2 changes: 2 additions & 0 deletions docs/how/cmake_dll/cmake/CMakeTestTSLANGCompiler.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Skip the actual test compile; assume the compiler works.
set(CMAKE_TSLANG_COMPILER_WORKS TRUE)
3 changes: 3 additions & 0 deletions docs/how/cmake_dll/dummy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main() {
return 0;
}
19 changes: 19 additions & 0 deletions docs/how/cmake_dll/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"]
}
18 changes: 18 additions & 0 deletions docs/how/cmake_dll/tslang.natvis
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

<!-- TypeScript -->

<Type Name="array&lt;*&gt;">
<DisplayString>array of {length} elements</DisplayString>
<Expand>
<Item Name="[length]">length</Item>
<ArrayItems>
<Size>length</Size>
<ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>

</AutoVisualizer>

44 changes: 44 additions & 0 deletions docs/how/cmake_dll/types/tslang/index.d.ts
Original file line number Diff line number Diff line change
@@ -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<T> = any
type Reference<T> = Ref<T> // deprecated alias of Ref

declare function Ref<T>(r: T): Ref<T>;
declare function Deref<T>(r: Ref<T>): T;

// deprecated aliases of Ref / Deref
declare function ReferenceOf<T>(r: T): Ref<T>;
declare function LoadReference<T>(r: Ref<T>): T;

declare function sizeof<T>(v?: T): index;
3 changes: 3 additions & 0 deletions docs/how/cmake_dll/wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function adder(a = 0, b = 0) {
return a + b;
}
2 changes: 1 addition & 1 deletion tslang/include/TypeScript/VSCodeTemplate/Files.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ const auto CMAKE_TSLANG_INFORMATION_DATA = R"raw(# The actual compile command.
# <DEFINES> <INCLUDES> optional
if(NOT CMAKE_TSLANG_COMPILE_OBJECT)
set(CMAKE_TSLANG_COMPILE_OBJECT
"<CMAKE_TSLANG_COMPILER> <FLAGS> --default-lib-path=${CMAKE_TSLANG_DIR} --emit=obj --export=none -o=<OBJECT> <SOURCE>")
"<CMAKE_TSLANG_COMPILER> <FLAGS> --default-lib-path=${CMAKE_TSLANG_DIR} --emit=obj -o=<OBJECT> <SOURCE>")
endif()

# How CMake links TSLANG objects into an executable/library.
Expand Down
Loading