diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 49494967e94243c47d41a5d61c8cd429e5a97379..0ec61a2d46e0625b5b7297aa236c9befe8669624 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -5,6 +5,9 @@ on:
   schedule:
     - cron: '0 1 * * 6'
 
+env:
+  BUILD_TYPE: RelWithDebInfo
+
 jobs:
   build:
     name: ${{ matrix.os }}
@@ -14,8 +17,20 @@ jobs:
         os: [ubuntu-latest, windows-latest, macOS-latest]
 
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v3
+
     - name: Compile
       run: make
+
     - name: Test
       run: make test
+
+    - name: Configure CMake
+      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+    - name: Build
+      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
+
+    - name: Test
+      working-directory: ${{github.workspace}}/build
+      run: ctest -C ${{env.BUILD_TYPE}}
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..813cb680a8e82ab411c57f7cced3cf58bf6e743c
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,129 @@
+cmake_minimum_required(VERSION 3.13)
+
+# Allow CMake 3.13+ to override options when using FetchContent/add_subdirectory.
+# option honors normal variables
+if (POLICY CMP0077)
+  cmake_policy(SET CMP0077 NEW)
+endif()
+
+# INTERFACE_LINK_LIBRARIES defines the link interface.
+if (POLICY CMP0022)
+  cmake_policy (SET CMP0022 NEW)
+endif()
+
+# PICT version, build, and product info
+set(PICT_VERSION_MAJOR 3 CACHE STRING "PICT major version")
+set(PICT_VERSION_MINOR 7 CACHE STRING "PICT minor version")
+set(PICT_VERSION_BUILD 3 CACHE STRING "PICT build version")
+set(PICT_VERSION_SHORT "${PICT_VERSION_MAJOR}.${PICT_VERSION_MINOR}")
+set(PICT_VERSION_FULL  "${PICT_VERSION_SHORT}.${PICT_VERSION_BUILD}")
+
+message(STATUS "PICT version ${PICT_VERSION_FULL}")
+
+# Set project name and properties
+project(
+  PICT
+  DESCRIPTION "Pairwise Independent Combinatorial Tool"
+  HOMEPAGE_URL "https://github.com/microsoft/pict"
+  LANGUAGES CXX
+  VERSION ${PICT_VERSION_FULL}
+)
+
+# Build configuration
+option(PICT_RUN_TESTS_ENABLED        "Enable running tests after build" ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON)
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+set(PICT_BUILD_OPTIONS PictBuildOptions)
+add_library(${PICT_BUILD_OPTIONS}
+  INTERFACE
+)
+
+if (MSVC)
+  # Enable multi-threaded build with MSVC
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+  target_compile_options(${PICT_BUILD_OPTIONS}
+    INTERFACE
+      /W3
+  )
+
+  target_compile_definitions(${PICT_BUILD_OPTIONS}
+    INTERFACE
+      UNICODE
+      WIN32_LEAN_AND_MEAN
+  )
+else()
+  target_compile_options(${PICT_BUILD_OPTIONS}
+    INTERFACE
+      -fPIC # Position-independent code: necessary for static libraries that are linked in dynamic libraries
+      -pipe
+      -fno-omit-frame-pointer
+      -fvisibility=hidden # By default, hide symbols on ELF binaries
+      -g # add debug symbols for build pdb
+  )
+
+  # AppleClang is not accepting -flto as linker option
+  if(NOT APPLE)
+    target_link_options(${PICT_BUILD_OPTIONS}
+      INTERFACE
+        $<IF:$<CONFIG:Debug>,,LINKER:-flto$<COMMA>-flto-partition=none> #Enable LTO
+        $<IF:$<CONFIG:Debug>,,LINKER:--no-undefined> # Have Linker error out if any symbols are undefined.
+    )
+  endif()
+endif()
+
+target_compile_definitions(${PICT_BUILD_OPTIONS}
+  INTERFACE
+    $<IF:$<CONFIG:Debug>,_DEBUG,NDEBUG>
+)
+
+add_subdirectory (api)
+add_subdirectory (cli)
+add_subdirectory (clidll)
+
+if(PICT_RUN_TESTS_ENABLED)
+  enable_testing()
+  add_subdirectory(test)
+endif()
+
+if(WIN32)
+  add_subdirectory(api-usage)
+  add_subdirectory(clidll-usage)
+endif()
+
+if(WIN32)
+  set(CPACK_GENERATOR ZIP NUGET)
+else()
+  set(CPACK_GENERATOR TGZ)
+endif()
+
+set(CPACK_SOURCE_GENERATOR "TGZ")
+set(CPACK_SOURCE_IGNORE_FILES
+  \\.git/
+  build/
+  ".*~$"
+)
+set(CPACK_VERBATIM_VARIABLES YES)
+
+execute_process(
+  COMMAND git log --pretty=format:%H -n 1
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  OUTPUT_VARIABLE GIT_COMMIT_HASH
+  OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+execute_process(
+  COMMAND git log --pretty=format:%h -n 1
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  OUTPUT_VARIABLE GIT_SHORT_COMMIT_HASH
+  OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+set(CPACK_SOURCE_PACKAGE_FILE_NAME
+  ${CMAKE_PROJECT_NAME}-${GIT_SHORT_COMMIT_HASH}
+)
+
+include(CPack)
diff --git a/README.md b/README.md
index c73a814afbdaf4546bfc790c43bf937b1cab1e19..88a204c560d55573e1b85f8a8b87c972efc0ad46 100644
--- a/README.md
+++ b/README.md
@@ -42,13 +42,14 @@ The test script produces a log: **dbg.log** or **rel.log** for the Debug and Rel
 
 >There are tests which randomize output which typically make it different on each run. These results should be masked in the baseline but currently aren't.
 
-## Building with clang++ on Linux, OS/X, *BSD, etc.
-Install clang through your package manager (most systems), Xcode (OS/X), or from the [LLVM website](http://llvm.org/releases/).
-On Linux, you also need to install recent GNU's "libstdc++" or LLVM's "libc++".
-
-Run `make` to build the `pict` binary.
-
-Run `make test` to run the tests as described above (requires Perl).
+## Building on Linux, OS/X, *BSD, etc.
+PICT uses CMake to build on Linux.
+Assuming installation of CMake and C++ toolchain, following set of commands will build and run tests in the directory `build`
+```
+> cmake -DCMAKE_BUILD_TYPE=Release -S . -B build
+> cmake --build build
+> pushd build && ctest -v && popd
+```
 
 ## Debugging
 
diff --git a/api-usage/CMakeLists.txt b/api-usage/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9cf1d3747252cdc6749a9e000c5dde2bceb1af5a
--- /dev/null
+++ b/api-usage/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(target_name
+  pict_api_usage
+)
+
+set(pict_api_usage_output
+  pictapisamples
+)
+
+set(pict_api_usage_src
+  ${CMAKE_CURRENT_SOURCE_DIR}/pictapi-sample.cpp
+)
+
+add_executable(${target_name}
+  ${pict_api_usage_src}
+)
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
+  FILES
+    ${pict_api_usage_src}
+)
+
+target_link_libraries(${target_name}
+  PRIVATE
+    PictBuildOptions
+    pict_api
+)
+
+set_target_properties(${target_name}
+  PROPERTIES
+    FOLDER api-usage
+    OUTPUT_NAME ${pict_api_usage_output}
+)
diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e618acd60b7f0e8dfe70b628144d6e2fc3949159
--- /dev/null
+++ b/api/CMakeLists.txt
@@ -0,0 +1,56 @@
+set(target_name
+  pict_api
+)
+
+set(pict_api_output
+  pict
+)
+
+set(pict_api_inc
+  ${CMAKE_CURRENT_SOURCE_DIR}/deriver.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/generator.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/pictapi.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/resource.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/trie.h
+)
+
+set(pict_api_src
+  ${CMAKE_CURRENT_SOURCE_DIR}/combination.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/deriver.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/exclusion.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/model.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/parameter.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/pictapi.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/task.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/worklist.cpp
+)
+
+add_library(${target_name}
+  STATIC
+    ${pict_api_inc}
+    ${pict_api_src}
+)
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
+  FILES
+    ${pict_api_inc}
+    ${pict_api_src}
+)
+
+target_include_directories(${target_name}
+  INTERFACE
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  PRIVATE
+    ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+target_link_libraries(${target_name}
+  PRIVATE
+    PictBuildOptions
+)
+
+set_target_properties(${target_name}
+  PROPERTIES
+    FOLDER src
+    OUTPUT_NAME ${pict_api_output}
+)
diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e849625de53025a856fb98a9909cea25a5e0f675
--- /dev/null
+++ b/cli/CMakeLists.txt
@@ -0,0 +1,83 @@
+include(GNUInstallDirs)
+
+set(target_name
+  pict_cli
+)
+
+set(pict_cli_output
+  pict
+)
+
+set(pict_cli_inc
+  ${CMAKE_CURRENT_SOURCE_DIR}/ccommon.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/cmdline.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/common.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/cparser.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/ctokenizer.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/gcd.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/gcdexcl.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/gcdmodel.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/model.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/resource.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/strings.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/ver.h
+)
+
+set(pict_cli_src
+  ${CMAKE_CURRENT_SOURCE_DIR}/ccommon.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/cmdline.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/common.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/cparser.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/ctokenizer.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/gcd.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/gcdexcl.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/gcdmodel.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/model.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/mparser.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/pict.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/strings.cpp
+)
+
+set(pict_cli_dll_src
+  ${pict_cli_src} PARENT_SCOPE
+)
+
+set(pict_cli_rc
+  $<$<BOOL:${WIN32}>:${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc>
+)
+
+add_executable(${target_name}
+  ${pict_cli_inc}
+  ${pict_cli_src}
+  ${pict_cli_rc}
+)
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
+  FILES
+    ${pict_cli_inc}
+    ${pict_cli_src}
+)
+
+target_link_libraries(${target_name}
+  PRIVATE
+    PictBuildOptions
+    pict_api
+)
+
+set_target_properties(${target_name}
+  PROPERTIES
+    FOLDER src
+    OUTPUT_NAME ${pict_cli_output}
+)
+
+install(
+  PROGRAMS $<TARGET_FILE:${target_name}>
+  TYPE BIN
+)
+
+install(
+  FILES
+    ${CMAKE_SOURCE_DIR}/doc/pict.md
+    ${CMAKE_SOURCE_DIR}/LICENSE.TXT
+  TYPE DOC
+)
diff --git a/clidll-usage/CMakeLists.txt b/clidll-usage/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..94f4953d52696599d710422a63854b4c82262705
--- /dev/null
+++ b/clidll-usage/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(target_name
+  pict_dll_usage
+)
+
+set(pict_dll_usage_output
+  pictclidllusage
+)
+
+set(pict_dll_usage_src
+  ${CMAKE_CURRENT_SOURCE_DIR}/pictclidll-sample.cpp
+)
+
+add_executable(${target_name}
+  ${pict_dll_usage_src}
+)
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
+  FILES
+    ${pict_dll_usage_src}
+)
+
+target_link_libraries(${target_name}
+  PRIVATE
+    PictBuildOptions
+    pict_dll
+)
+
+set_target_properties(${target_name}
+  PROPERTIES
+    FOLDER clidll-usage
+    OUTPUT_NAME ${pict_dll_usage_output}
+)
diff --git a/clidll/CMakeLists.txt b/clidll/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5545659fc2f9c4ecdb9dd56acd67de48a138275c
--- /dev/null
+++ b/clidll/CMakeLists.txt
@@ -0,0 +1,47 @@
+set(target_name
+  pict_dll
+)
+
+set(pict_dll_output
+  pict
+)
+
+set(pict_dll_inc
+  ${CMAKE_CURRENT_SOURCE_DIR}/resource.h
+)
+
+set(pict_dll_src
+  ${CMAKE_CURRENT_SOURCE_DIR}/dllmain.cpp
+  $<$<BOOL:${WIN32}>:${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc>
+  $<$<BOOL:${WIN32}>:${CMAKE_CURRENT_SOURCE_DIR}/pictclidll.def>
+)
+
+add_library(${target_name}
+  SHARED
+    ${pict_dll_inc}
+    ${pict_dll_src}
+    ${pict_cli_dll_src}
+)
+
+add_compile_definitions(${target_name}
+  PRIVATE
+    PICTCLIDLL_EXPORTS
+)
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
+  FILES
+    ${pict_dll_inc}
+    ${pict_dll_src}
+)
+
+target_link_libraries(${target_name}
+  PRIVATE
+    PictBuildOptions
+    pict_api
+)
+
+set_target_properties(${target_name}
+  PROPERTIES
+    FOLDER src
+    OUTPUT_NAME ${pict_dll_output}
+)
diff --git a/clidll/dllmain.cpp b/clidll/dllmain.cpp
index d8b5f320b8a9b2fe085c71bcf3e6e79a08b27f54..1ea5a226b3b54bd394d0177baaaf701e35e540b8 100644
--- a/clidll/dllmain.cpp
+++ b/clidll/dllmain.cpp
@@ -1,3 +1,4 @@
+#if defined(_WIN32)
 #include "windows.h"
 
 BOOL APIENTRY DllMain( HMODULE hModule,
@@ -15,3 +16,4 @@ BOOL APIENTRY DllMain( HMODULE hModule,
     }
     return TRUE;
 }
+#endif // defined(_WIN32)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..627da53bf4b3ae8eedac100fd0ec54bff72d2f2b
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_test(
+  NAME run_pict
+  COMMAND perl test.pl $<TARGET_FILE:pict_cli> $<IF:$<CONFIG:Debug>,dbg,rel>.log
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+)