# Instructions for adding OW DOS targets to VSCode

## Prerequisites

Install [Open Watcom V2](https://github.com/open-watcom/open-watcom-v2) with the DOS target operating system. The compiler does **not** need to be on the system PATH — the CMake presets below handle all environment setup.

| Host OS | Example install path | Compiler binaries |
| ------- | -------------------- | ----------------- |
| Windows | `C:\WATCOM` | `binnt64\` (64-bit host) or `binnt\` (32-bit host) |
| Linux | `/opt/watcom` | `binl64/` (64-bit host) or `binl/` (32-bit host) |

> **Note:** The paths above are examples. Adjust the `WATCOM` environment variable in `CMakePresets.json` and `c_cpp_properties.json` to match your actual install location.

Install [Visual Studio Code](https://code.visualstudio.com/) with these extensions:

- **C/C++ Extension Pack** (`ms-vscode.cpptools-extension-pack`) — includes C/C++ IntelliSense, CMake Tools, and CMake language support
- **CMake Language Support** (`twxs.cmake`) — syntax highlighting for CMakeLists.txt

> **Tip:** Add an `.vscode/extensions.json` to your project so VS Code prompts collaborators to install the required extensions automatically:
>
> ```json
> {
>     "recommendations": [
>         "ms-vscode.cpptools-extension-pack",
>         "twxs.cmake"
>     ]
> }
> ```

Requires **CMake 3.21+** (for preset `condition` support and the Watcom WMake generator).

## Project layout

```tree
my-project/
├── .vscode/
│   ├── c_cpp_properties.json   ← IntelliSense configuration
│   ├── extensions.json         ← recommended extensions
│   └── settings.json           ← CMake Tools settings
├── include/
│   └── myheader.h
├── src/
│   └── main.c
├── CMakeLists.txt
└── CMakePresets.json            ← replaces user-local CMake Kits
```

## Configuration

### 1. CMakePresets.json (replaces CMake Kits)

Instead of editing the user-local `cmake-tools-kits.json`, create a `CMakePresets.json` in the project root. This is **portable** (committed to version control), **cross-platform** (Linux and Windows), and requires no per-user setup.

The presets are organized in a hierarchy:

```graph
default (shared binaryDir)
├── dos-host-linux   (Linux paths/env, hidden)
├── dos-host-windows (Windows paths/env, hidden)
└── dos-base (inherits both host presets; generator, common cache vars)
    ├── dos32-base (wcl386 compiler, hidden)
    │   ├── dos32-debug
    │   └── dos32-release
    └── dos16-base (wcl compiler, I86 processor, hidden)
        ├── dos16-debug
        └── dos16-release
```

Only presets without `"hidden": true` appear in VS Code's preset picker.

```json
{
    "version": 6,
    "configurePresets": [
        {
            "name": "default",
            "hidden": true,
            "binaryDir": "${sourceDir}/build"
        },
        {
            "name": "dos-host-linux",
            "hidden": true,
            "inherits": "default",
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Linux"
            },
            "environment": {
                "WATCOM": "/opt/watcom",
                "WATCOM_BINDIR": "$env{WATCOM}/binl64",
                "PATH": "$env{WATCOM}/binl64:$env{WATCOM}/binl:$penv{PATH}"
            }
        },
        {
            "name": "dos-host-windows",
            "hidden": true,
            "inherits": "default",
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Windows"
            },
            "environment": {
                "WATCOM": "C:/WATCOM",
                "WATCOM_BINDIR": "$env{WATCOM}/binnt64",
                "Path": "$env{WATCOM}/binnt64;$env{WATCOM}/binnt;$penv{Path}"
            }
        },
        {
            "name": "dos-base",
            "hidden": true,
            "inherits": ["dos-host-linux", "dos-host-windows"],
            "generator": "Watcom WMake",
            "environment": {
                "EDPATH": "$env{WATCOM}/eddat",
                "INCLUDE": "$env{WATCOM}/h"
            },
            "cacheVariables": {
                "CMAKE_SYSTEM_NAME": "DOS",
                "CMAKE_C_COMPILER_WORKS": "1",
                "CMAKE_CXX_COMPILER_WORKS": "1"
            }
        },
        {
            "name": "dos32-base",
            "hidden": true,
            "inherits": "dos-base",
            "cacheVariables": {
                "CMAKE_SYSTEM_PROCESSOR": "x86",
                "CMAKE_C_COMPILER": "$env{WATCOM_BINDIR}/wcl386",
                "CMAKE_CXX_COMPILER": "$env{WATCOM_BINDIR}/wcl386"
            }
        },
        {
            "name": "dos16-base",
            "hidden": true,
            "inherits": "dos-base",
            "cacheVariables": {
                "CMAKE_SYSTEM_PROCESSOR": "I86",
                "CMAKE_C_COMPILER": "$env{WATCOM_BINDIR}/wcl",
                "CMAKE_CXX_COMPILER": "$env{WATCOM_BINDIR}/wcl"
            }
        },
        {
            "name": "dos32-debug",
            "displayName": "DOS 32-bit (Debug)",
            "inherits": "dos32-base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug"
            }
        },
        {
            "name": "dos32-release",
            "displayName": "DOS 32-bit (Release)",
            "inherits": "dos32-base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release"
            }
        },
        {
            "name": "dos16-debug",
            "displayName": "DOS 16-bit (Debug)",
            "inherits": "dos16-base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug"
            }
        },
        {
            "name": "dos16-release",
            "displayName": "DOS 16-bit (Release)",
            "inherits": "dos16-base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release"
            }
        }
    ],
    "buildPresets": [
        {
            "name": "dos32-debug",
            "displayName": "Build DOS 32-bit (Debug)",
            "configurePreset": "dos32-debug"
        },
        {
            "name": "dos32-release",
            "displayName": "Build DOS 32-bit (Release)",
            "configurePreset": "dos32-release"
        },
        {
            "name": "dos16-debug",
            "displayName": "Build DOS 16-bit (Debug)",
            "configurePreset": "dos16-debug"
        },
        {
            "name": "dos16-release",
            "displayName": "Build DOS 16-bit (Release)",
            "configurePreset": "dos16-release"
        }
    ]
}
```

**Key points:**

- Adjust the `WATCOM` environment values in `dos-host-linux` and `dos-host-windows` to match your actual install paths.
- `"condition"` blocks ensure only the matching host preset activates — a Linux developer and a Windows developer share the same file.
- `CMAKE_C_COMPILER_WORKS` / `CMAKE_CXX_COMPILER_WORKS` are set as `cacheVariables` in the preset (not as `-D` flags in workspace settings).
- `WATCOM_BINDIR` is derived from `WATCOM`, so compiler paths adapt automatically.
- `dos-base` inherits from **both** host presets; the inactive one is skipped by its condition.

### 2. VS Code settings (.vscode/settings.json)

With presets, the workspace settings are minimal:

```json
{
    "cmake.useCMakePresets": "always",
    "C_Cpp.intelliSenseEngine": "default"
}
```

That's it. No `cmake.configureArgs`, no `cmake.generator` override — the presets handle everything.

### 3. IntelliSense configuration (.vscode/c_cpp_properties.json)

The MSVC IntelliSense engine cannot interrogate the Watcom compiler, so it needs help. This configuration combines two sources of information:

1. **CMake Tools (`configurationProvider`)** supplies compiler flags and auto-discovered macros from the CMake File API.
2. **Manual `defines`** provide shims for Watcom-specific keywords that IntelliSense cannot parse.

With `"mergeConfigurations": true`, both sources are merged together.

```jsonc
{
    // Set WATCOM to your Open Watcom install path.
    // Example: "C:/WATCOM" on Windows, "/opt/watcom" on Linux.
    "env": {
        "WATCOM": "/opt/watcom"
    },
    "configurations": [
        {
            "name": "Watcom DOS",
            "includePath": [
                "${workspaceFolder}/include/**",
                "${WATCOM}/h/**"
            ],
            // CMake Tools supplies compiler flags and Watcom built-in macros
            // (auto-discovered via -pm in CMakeLists.txt) based on the active
            // preset. Switching presets automatically updates IntelliSense.
            "configurationProvider": "ms-vscode.cmake-tools",
            // Merge our manual shims on top of what CMake Tools reports,
            // so both the auto-discovered macros and the shims take effect.
            "mergeConfigurations": true,
            // IntelliSense-only shims for Watcom constructs that the MSVC
            // IntelliSense engine cannot parse. These must NOT be passed to
            // the real compiler.
            "defines": [
                // Watcom pragma keywords — remap to MSVC push/pop so
                // #pragma pack(__push, N) parses correctly
                "__push=push",
                "__pop=pop",
                // Calling-convention macros that resolve to
                // __declspec(__watcall) in _comdef.h. Emptying them avoids
                // IntelliSense errors. Safe because _comdef.h guards each
                // with #ifndef.
                "_WCRTLINK=",
                "_WCIRTLINK=",
                "_WCRTDATA=",
                "_WPRTLINK=",
                "_WMRTLINK=",
                // Watcom compiler keywords used throughout the headers
                // (_comdef.h maps _WCNEAR->__near, _WCFAR->__far, etc.).
                // IntelliSense doesn't understand these, so they are
                // defined to nothing.
                "__near=",
                "__far=",
                "__huge=",
                "__interrupt=",
                "__fortran=",
                "__watcall=",
                "__loadds=",
                "__saveregs=",
                "__export=",
                "__far16="
            ],
            "cStandard": "c99",
            "cppStandard": "c++98",
            "intelliSenseMode": "msvc-x86",
            // Empty string disables built-in compiler querying. IntelliSense
            // cannot interrogate the Watcom compiler, so we rely entirely on
            // the configurationProvider + manual shims above.
            "compilerPath": ""
        }
    ],
    "version": 4
}
```

**Why not define `__WATCOMC__` manually?**

The CMakeLists.txt auto-discovers it (see below), and CMake Tools feeds it to IntelliSense via the `configurationProvider`. No need to hard-code it.

### 4. CMakeLists.txt

#### Basic structure

```cmake
cmake_minimum_required(VERSION 3.21)
project(MyProject LANGUAGES C CXX VERSION 0.1.0)

set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 98)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
```

#### OpenWatcom compiler options

```cmake
if(CMAKE_C_COMPILER_ID STREQUAL "OpenWatcom")
    add_compile_options(
        -zq         # Operate quietly (display only error messages)
        -wx         # Maximum warning level
        -fr=nul     # Set error file to NUL (discard error file output)
    )

    if(CMAKE_SYSTEM_PROCESSOR STREQUAL "I86")
        # 16-bit DOS
        add_compile_options(
            -bt=dos     # Build target is DOS
            -ms         # Small memory model (small code/small data)
            -0          # 8086 instructions
        )
    else()
        # 32-bit DOS (dos4gw extender)
        add_compile_options(
            -bt=dos4g   # Build target is DOS/4GW protected mode
            -5s         # Pentium stack calling conventions
            -s          # Remove stack overflow checks
        )
    endif()

    string(JOIN " " CMAKE_C_FLAGS
        ${CMAKE_C_FLAGS}
        -za99       # Disable extensions, use C99 standard
    )
    string(JOIN " " CMAKE_CXX_FLAGS
        ${CMAKE_CXX_FLAGS}
        -ze         # Enable extensions (near, far, export, etc.)
    )
endif()
```

#### Target definition

```cmake
add_executable(${PROJECT_NAME}
    ${CMAKE_SOURCE_DIR}/src/main.c
)

target_include_directories(${PROJECT_NAME} PRIVATE
    ${CMAKE_SOURCE_DIR}/include
)

# 8.3-safe output name for DOS compatibility
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "myproj")
```

#### Auto-discovering Watcom built-in macros for IntelliSense

Open Watcom defines macros internally (e.g. `__WATCOMC__`, `_M_IX86`, `__DOS__`, `__386__`) but does not expose them to CMake. This means the CMake File API — and by extension, the CMake Tools `configurationProvider` in VS Code — cannot discover them. IntelliSense will show false errors for any code that depends on these macros.

The fix: query the compiler with `-pm` (print macros) using the same target flags, parse the output, and re-inject them as compile definitions. This is a harmless no-op for the real compiler (same-value redefinition) and makes the macros visible to IntelliSense.

```cmake
if(CMAKE_C_COMPILER_ID STREQUAL "OpenWatcom")
    # Collect all compile options so the -pm dump reflects the actual build
    # configuration. Resolve generator expressions for the active build type
    # since execute_process runs at configure time.
    get_directory_property(_WC_ALL_OPTIONS COMPILE_OPTIONS)
    set(_WC_PM_FLAGS -pm)
    foreach(_opt IN LISTS _WC_ALL_OPTIONS)
        if(_opt MATCHES "^\\$<\\$<CONFIG:${CMAKE_BUILD_TYPE}>:(.+)>$")
            list(APPEND _WC_PM_FLAGS "${CMAKE_MATCH_1}")
        elseif(NOT _opt MATCHES "^\\$<")
            list(APPEND _WC_PM_FLAGS "${_opt}")
        endif()
    endforeach()

    execute_process(
        COMMAND ${CMAKE_C_COMPILER} ${_WC_PM_FLAGS} /dev/null
        OUTPUT_VARIABLE _WC_PM_OUTPUT
        RESULT_VARIABLE _WC_PM_RESULT
        ERROR_VARIABLE _WC_PM_ERROR
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    message(VERBOSE "[Watcom -pm] Command: ${CMAKE_C_COMPILER} ${_WC_PM_FLAGS} /dev/null")
    message(VERBOSE "[Watcom -pm] Exit code: ${_WC_PM_RESULT}")
    if(_WC_PM_ERROR)
        message(VERBOSE "[Watcom -pm] Stderr: ${_WC_PM_ERROR}")
    endif()
    message(VERBOSE "[Watcom -pm] Output:\n${_WC_PM_OUTPUT}")

    # Parse "#define NAME VALUE" lines into NAME=VALUE compile definitions
    string(REGEX MATCHALL "#define [^\n]+" _WC_MACRO_LINES "${_WC_PM_OUTPUT}")
    set(_WC_DEFINES "")
    foreach(_line IN LISTS _WC_MACRO_LINES)
        if(_line MATCHES "^#define ([^ ]+) (.+)$")
            list(APPEND _WC_DEFINES "${CMAKE_MATCH_1}=${CMAKE_MATCH_2}")
        elseif(_line MATCHES "^#define ([^ ]+)$")
            list(APPEND _WC_DEFINES "${CMAKE_MATCH_1}")
        endif()
    endforeach()

    target_compile_definitions(${PROJECT_NAME} PRIVATE ${_WC_DEFINES})
endif()
```

> **Note (Windows):** Replace `/dev/null` with `NUL` if you are not using a cross-platform wrapper. A portable alternative is to create an empty file and pass its path instead.

## Building

### From the command line

```bash
# Configure (pick a preset; --fresh for clean reconfigure)
cmake --preset dos32-debug --fresh

# Build
cmake --build --preset dos32-debug
```

### From VS Code

1. Open the project folder in VS Code.
2. If prompted *"Do you trust the authors of the files in this folder?"*, click **Yes, I trust the authors**.
3. CMake Tools will detect `CMakePresets.json` and prompt to select a configure preset. Choose one (e.g. **DOS 32-bit (Debug)**).
4. Use `Ctrl+Shift+P` → **CMake: Build** (or press `F7`) to build.

Switching between 16-bit and 32-bit targets is as simple as selecting a different preset — IntelliSense updates automatically because `mergeConfigurations` picks up the new macros from the CMake File API.

## Useful VS Code commands (Ctrl+Shift+P)

| Command | Purpose |
| ------- | ------- |
| `CMake: Select Configure Preset` | Switch between dos32-debug, dos16-release, etc. |
| `CMake: Build` | Build the current preset (also `F7`) |
| `CMake: Delete Cache and Reconfigure` | Clean reconfigure if things get stuck |
| `CMake: Reset CMake Tools Extension State` | Nuclear option — resets all CMake Tools state |
| `C/C++: Edit Configurations (JSON)` | Edit `c_cpp_properties.json` |
| `Preferences: Open Workspace Settings (JSON)` | Edit `.vscode/settings.json` |

## Migration from CMake Kits

If you previously followed the old instructions using user-local CMake Kits:

1. Remove the Open Watcom entries from your user-local `cmake-tools-kits.json` (`Ctrl+Shift+P` → `CMake: Edit User-Local CMake Kits`).
2. Remove `cmake.configureArgs`, `cmake.generator`, and `C_Cpp.default.configurationProvider` from `.vscode/settings.json`.
3. Add the `CMakePresets.json`, updated `settings.json`, and updated `c_cpp_properties.json` files shown above.
4. Restart VS Code.
