Skip to content

OW tools usage with VSCode

PlayDay edited this page Mar 30, 2026 · 10 revisions

Instructions for adding OW DOS targets to VSCode

Prerequisites

Install 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 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:

{
    "recommendations": [
        "ms-vscode.cpptools-extension-pack",
        "twxs.cmake"
    ]
}

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

Project layout

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:

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.

{
    "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:

{
    "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.

{
    // 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_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

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

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.

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

# 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+PCMake: 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+PCMake: 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.

Clone this wiki locally