Tcl 9.1 Static Build
Table of Contents
- 1. Introduction
- 2. Getting Started
- 3. CMake Variables
- 4. Implementation
- 4.1. Preamble
- 4.2. Check for zip
- 4.3. Check for libtool
- 4.4. Set Tcl/Tk version
- 4.5. Set Tcl Standard Library Versions
- 4.6. Set Tcl/Tk Source URLs
- 4.7. Set Critcl Source URL
- 4.8. Set Tcllib Source URL
- 4.9. Make Tcl/Tk sources available at configure time
- 4.10. Kick off a Unix or Window build
- 4.11. Linux and MacOS
- 4.12. Windows
- 4.13. Utilities
- 4.13.1. Concatenate and normalize a list of paths
- 4.13.2. Convert path to absolute path
- 4.13.3. Check Tcl Version
- 4.13.4. Build Tcl at configure time
- 4.13.5. Build Tcl
- 4.13.6. Build Tcl (Windows)
- 4.13.7. Symlink Tcl
- 4.13.8. Symlink Tcl (Windows)
- 4.13.9. Build Critcl
- 4.13.10. Build Tcllib
- 4.13.11. Build TclLib (Windows)
- 4.13.12. Build Tk
- 4.13.13. Build Tk (Windows)
- 4.13.14. Symlink Tk
- 4.13.15. Symlink Tk (Windows)
1. Introduction
This CMake recipe builds and installs Tcl/Tk 9 from scratch on Windows, Linux and MacOS. The Tcl and Tk interpreters tclsh and wish are built statically across platforms and can be installed in a local workspace with no other requirements. The goal is to
provide a convenient environment to build and even deploy Tcl and Tk GUI applications without having set up any dependencies or rely on package managers.
The Tcl Standard Library is also installed along with the parts accelerated by Critcl. This includes many useful libraries that are not part of the standard Tcl distribution like encoding/decoding JSON.
In addition a static version of the Tcl interpreter is also built at configure time. This allows using this recipe to also be used as part of a CMake super build in which subsequent steps have full access to Tcl which is much easier to work with than CMake. As an example, it is used in this recipe to check the version of the Tcl interpreter to avoid rebuilding it.
2. Getting Started
2.1. Linux and MacOS
Requirements:
cmake> 3.14gcc(tested withgcc 15)- Autoconf
- make
Assuming this repo has been cloned to tcl-static-build this should download Tcl/Tk sources, build and then install them to ~/MyWorkspace/bin/:
> cd tcl-static-build > mkdir build > cd build > cmake .. -DCMAKE_INSTALL_PREFIX=~/MyWorkspace -DTCL_STATIC_BUILD_TK=On > make
And checking the runtime dependencies …:
> ldd ~/MyWorkspace/bin/tclsh
linux-vdso.so.1 (0x00007fb0c6af1000)
libz.so.1 => /usr/lib/libz.so.1 (0x00007fb0c6a9f000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007fb0c66e2000)
libtommath.so.1 => /usr/lib/libtommath.so.1 (0x00007fb0c6a87000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fb0c64f1000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fb0c6af3000)
> ldd ~/MyWorkspace/bin/wish9.1
linux-vdso.so.1 (0x00007ff057210000)
libXft.so.2 => /usr/lib/libXft.so.2 (0x00007ff056de6000)
libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x00007ff056d95000)
libX11.so.6 => /usr/lib/libX11.so.6 (0x00007ff056c53000)
... ( a whole bunch of X11/Wayland runtime dependencies, standard for GUI applications )
2.2. Windows
Requirements:
Please make sure to start the build in Visual Studio Developer Command Prompt, this ensures required tools like nmake and vcc available at the command prompt which is a lot easier than hunting down explicit paths.
Assuming this repo has been cloned to tcl-static-build this should download Tcl/Tk sources, build and install them to C:\Users\<your-username>\Tcl\bin:
> cd tcl-static-build > mkdir build > cd build > cmake.exe .. -DCMAKE_INSTALL_PREFIX=C:\Users\<your-username>\Tcl -DTCL_STATIC_BUILD_TK=On > MSBuild.exe ALL_BUILD.vcxproj
And checking the runtime dependencies …:
> cd C:\Users\<your-username>\Tcl\bin
> dumpbin.exe /DEPENDENTS tclsh.exe
Microsoft (R) COFF/PE Dumper Version 14.39.33521.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file tclsh91s.exe
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
VCRUNTIME140.dll
KERNEL32.dll
ADVAPI32.dll
NETAPI32.dll
USER32.dll
USERENV.dll
WS2_32.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-environment-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll
api-ms-win-crt-utility-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
Summary
3000 .data
13000 .pdata
A2000 .rdata
3000 .reloc
F000 .rsrc
1CD000 .text
> dumpbin.exe /DEPENDENTS wish91s.exe
Microsoft (R) COFF/PE Dumper Version 14.39.33521.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file wish91s.exe
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
api-ms-win-crt-runtime-l1-1-0.dll
WS2_32.dll
OLEAUT32.dll
COMDLG32.dll
COMCTL32.dll
IMM32.dll
UxTheme.dll
VCRUNTIME140.dll
KERNEL32.dll
ADVAPI32.dll
NETAPI32.dll
GDI32.dll
USER32.dll
USERENV.dll
WINSPOOL.DRV
SHELL32.dll
ole32.dll
OLEACC.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-environment-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-utility-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
Summary
7000 .data
20000 .pdata
FB000 .rdata
7000 .reloc
23000 .rsrc
2FB000 .text
3. CMake Variables
| Variable | Default Value | Documentation |
|---|---|---|
TCL_STATIC_BUILD_BUILD_AND_INSTALL_TCL_TK |
On | Whether to build and install Tcl and Tk. This recipe can be used to build Tcl only at configure time |
| so it can be part of a super build which needs Tcl in some subsequent step without installing it at build time. | ||
TCL_STATIC_BUILD_TCL_LOCAL_SOURCES |
“” | Use a local copy of Tcl sources for offline builds |
TCL_STATIC_BUILD_CRITCL_LOCAL_SOURCES |
“” | Use a local copy of Critcl sources |
TCL_STATIC_BUILD_TCLLIB_LOCAL_SOURCES |
“” | Use a local copy of TclLib, the Tcl standard library |
TCL_STATIC_BUILD_TK_LOCAL_SOURCES |
“” | Use a local copy of Tk sources for offline builds |
TCL_STATIC_BUILD_TK |
Off | Optionally build Tk, only required for GUI applications |
TCL_STATIC_BUILD_UNIX_TCLSH |
Configure time location of the Tcl interpreter on Unix | Path to the configure time location of tclsh for Unix and MacOs builds |
TCL_STATIC_BUILD_WIN_TCLSH |
Configure time location of the Tcl interperter on Windows | Path to the configure time location of tclsh for Windows builds |
4. Implementation
4.1. Preamble
cmake_minimum_required(VERSION 3.14)
project(tcl-static-build)
include(ExternalProject)
include(FetchContent)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/modules")
include(Utils)
if(NOT (MSVC OR APPLE OR UNIX))
message(FATAL_ERROR "This build currenly works only with macOS, Microsoft Visual Studio and Linux.")
endif()
TCL_STATIC_BUILD_BUILD_AND_INSTALL_TCL_TK toggles whether to build and install Tcl/Tk, if the user has not set it assume we do.
if(NOT DEFINED TCL_STATIC_BUILD_BUILD_AND_INSTALL_TCL_TK)
set(TCL_STATIC_BUILD_BUILD_AND_INSTALL_TCL_TK On)
endif()
4.2. Check for zip
In theory if zip is not available the Tcl/Tk sources ship with minizip, a small drop in replacement, but that doesn’t seem to work on Unix builds.
On most distros zip is available by default and if not (looking at you Arch) it is an easy enough dependency to add.
if(APPLE OR UNIX)
find_program(TCL_STATIC_BUILD_ZIP_EXECUTABLE NAMES zip)
if(NOT TCL_STATIC_BUILD_ZIP_EXECUTABLE)
message(FATAL_ERROR "Could not find 'zip' which is necessary for building with ZipFs support.")
endif()
endif()
4.3. Check for libtool
if(APPLE OR UNIX)
find_program(TCL_STATIC_BUILD_LIBTOOL libtool)
if(NOT TCL_STATIC_BUILD_LIBTOOL)
message(FATAL_ERROR "Could not find 'libtool' which is necessary to build static archives")
endif()
endif()
4.4. Set Tcl/Tk version
set(TCL_STATIC_BUILD_VERSION "9.1")
set(TCL_STATIC_ZIP_VERSION 91a1)
4.5. Set Tcl Standard Library Versions
set(TCL_STATIC_BUILD_CRITCL_VERSION "3.3.1")
set(TCL_STATIC_BUILD_TCLLIB_VERSION "2.0")
4.6. Set Tcl/Tk Source URLs
Check if the user has specified offline paths to the Tcl/Tk source otherwise get it from the official site.
if(TCL_STATIC_BUILD_TCL_LOCAL_SOURCES)
to_absolute_path(${TCL_STATIC_BUILD_TCL_LOCAL_SOURCES} TCL_STATIC_BUILD_URL)
else()
set(TCL_STATIC_BUILD_URL http://prdownloads.sourceforge.net/tcl/tcl${TCL_STATIC_ZIP_VERSION}-src.zip)
endif()
if(TCL_STATIC_BUILD_TK_LOCAL_SOURCES)
to_absolute_path(${TCL_STATIC_BUILD_TK_LOCAL_SOURCES} TK_STATIC_BUILD_URL)
else()
set(TK_STATIC_BUILD_URL http://prdownloads.sourceforge.net/tcl/tk${TCL_STATIC_ZIP_VERSION}-src.zip)
endif()
4.7. Set Critcl Source URL
if(TCL_STATIC_BUILD_CRITCL_LOCAL_SOURCES)
to_absolute_path(${TCL_STATIC_BUILD_CRITCL_LOCAL_SOURCES} CRITCL_STATIC_BUILD_URL)
else()
set(CRITCL_STATIC_BUILD_URL http://github.com/andreas-kupries/critcl/zipball/${TCL_STATIC_BUILD_CRITCL_VERSION})
endif()
4.8. Set Tcllib Source URL
if(TCL_STATIC_BUILD_TCLLIB_LOCAL_SOURCES)
to_absolute_path(${TCL_STATIC_BUILD_TCLLIB_LOCAL_SOURCES} CRITCL_STATIC_BUILD_URL)
else()
set(TCLLIB_STATIC_BUILD_URL https://core.tcl-lang.org/tcllib/uv/tcllib-${TCL_STATIC_BUILD_TCLLIB_VERSION}.zip)
endif()
4.9. Make Tcl/Tk sources available at configure time
4.9.1. Warn if install location is not overridden
This build is meant to be used in an isolated workspace so if CMAKE_INSTALL_PREFIX is still pointing to the system wide install location let the user know.
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
if(APPLE OR UNIX)
set(EXAMPLE_INSTALL_PREFIX "/home/user/MyWorkspace")
else()
set(EXAMPLE_INSTALL_PREFIX "C:\User\user\MyWorkspace")
endif()
message(WARNING "CMAKE_INSTALL_PREFIX is the default ${CMAKE_INSTALL_PREFIX}. It is recommended to override it to your local workspace, eg. 'cmake .. -DCMAKE_INSTALL_PREFIX=${EXAMPLE_INSTALL_PREFIX}'")
endif()
4.9.2. Download or Pull in Local Tcl/Tk sources
- Declare Tcl sources
set(TCL_SOURCES tcl${TCL_STATIC_BUILD_VERSION}) FetchContent_Declare( ${TCL_SOURCES} URL ${TCL_STATIC_BUILD_URL} DOWNLOAD_EXTRACT_TIMESTAMP FALSE ) - Declare Critcl sources
set(CRITCL_SOURCES critcl${TCL_STATIC_BUILD_CRITCL_VERSION}) FetchContent_Declare( ${CRITCL_SOURCES} URL ${CRITCL_STATIC_BUILD_URL} DOWNLOAD_EXTRACT_TIMESTAMP FALSE ) - Declare Tcllib sources
set(TCLLIB_SOURCES tcllib${TCL_STATIC_BUILD_TCLLIB_VERSION}) FetchContent_Declare( ${TCLLIB_SOURCES} URL ${TCLLIB_STATIC_BUILD_URL} DOWNLOAD_EXTRACT_TIMESTAMP FALSE ) - Declare Tk sources
if(TCL_STATIC_BUILD_TK) set(TK_SOURCES tk${TCL_STATIC_BUILD_VERSION}) FetchContent_Declare( ${TK_SOURCES} URL ${TK_STATIC_BUILD_URL} DOWNLOAD_EXTRACT_TIMESTAMP FALSE ) endif() - Download Tcl sources
if(NOT ${TCL_SOURCES}_POPULATED) if(NOT TCL_STATIC_BUILD_TCL_LOCAL_SOURCES) message(STATUS "Downloading Tcl sources: ${TCL_STATIC_BUILD_URL} ...") else() message(STATUS "Using local Tcl sources: ${TCL_STATIC_BUILD_URL}") endif() FetchContent_MakeAvailable(${TCL_SOURCES}) if(NOT TCL_STATIC_BUILD_TCL_LOCAL_SOURCES) message(STATUS "Completed download: ${${TCL_SOURCES}_SOURCE_DIR}") endif() endif() - Download Critcl sources
if(NOT ${CRITCL_SOURCES}_POPULATED) if(NOT TCL_STATIC_BUILD_CRITCL_LOCAL_SOURCES) message(STATUS "Downloading Critcl sources: ${CRITCL_STATIC_BUILD_URL} ...") else() message(STATUS "Using local Critcl sources: ${CRITCL_STATIC_BUILD_URL}") endif() FetchContent_MakeAvailable(${CRITCL_SOURCES}) if(NOT TCL_STATIC_BUILD_TCL_LOCAL_SOURCES) message(STATUS "Completed download: ${${CRITCL_SOURCES}_SOURCE_DIR}") endif() endif() - Download Tcllib sources
if(NOT ${TCLLIB_SOURCES}_POPULATED) if(NOT TCL_STATIC_BUILD_TCLLIB_LOCAL_SOURCES) message(STATUS "Downloading Tcllib sources: ${TCLLIB_STATIC_BUILD_URL} ...") else() message(STATUS "Using local Tcllib sources: ${TCLLIB_STATIC_BUILD_URL}") endif() FetchContent_MakeAvailable(${TCLLIB_SOURCES}) if(NOT TCL_STATIC_BUILD_TCL_LOCAL_SOURCES) message(STATUS "Completed download: ${${TCLLIB_SOURCES}_SOURCE_DIR}") endif() endif() - Download Tk sources
if(TCL_STATIC_BUILD_TK) if(NOT ${TK_SOURCES}_POPULATED) if(NOT TCL_STATIC_BUILD_TK_LOCAL_SOURCES) message(STATUS "Downloading Tk sources: ${TK_STATIC_BUILD_URL} ...") else() message(STATUS "Using local Tk sources: ${TK_STATIC_BUILD_URL}") endif() FetchContent_MakeAvailable(${TK_SOURCES}) if(NOT TCL_STATIC_BUILD_TK_LOCAL_SOURCES) message(STATUS "Completed download: ${${TK_SOURCES}_SOURCE_DIR}") endif() endif() endif()
4.10. Kick off a Unix or Window build
if(APPLE OR UNIX)
include(LinuxMacosBuild)
else()
include(Windows)
endif()
4.11. Linux and MacOS
4.11.1. Set configure time path to tclsh
set(TCL_STATIC_BUILD_TCLSH_EXE "tclsh9.1")
set(TCL_STATIC_BUILD_WISH_EXE "wish9.1")
normalize_path(TCL_STATIC_BUILD_UNIX_TCLSH ${FETCHCONTENT_BASE_DIR} "bin" ${TCL_STATIC_BUILD_TCLSH_EXE})
normalize_path(TCL_STATIC_BUILD_UNIX_INSTALLED_TCLSH ${CMAKE_INSTALL_PREFIX} "bin" ${TCL_STATIC_BUILD_TCLSH_EXE})
4.11.2. Build Tcl at configure time
if(EXISTS ${TCL_STATIC_BUILD_UNIX_TCLSH})
check_tcl_version_matches_required_version(TCL_SOURCES_TCLSH_MATCHES_REQUIRED ${TCL_STATIC_BUILD_UNIX_TCLSH})
if(${TCL_SOURCES_TCLSH_MATCHES_REQUIRED})
message(STATUS "The Tcl executable already exists: ${TCL_STATIC_BUILD_UNIX_TCLSH}")
else()
message(STATUS "Rebuilding Tcl since the existing executable is not the required version: ${TCL_STATIC_BUILD_VERSION}")
build_tcl_unix_at_configure_time(${${TCL_SOURCES}_SOURCE_DIR} ${FETCHCONTENT_BASE_DIR})
endif()
else()
build_tcl_unix_at_configure_time(${${TCL_SOURCES}_SOURCE_DIR} ${FETCHCONTENT_BASE_DIR})
endif()
4.11.3. Add Build Time Tcl Target
if(TCL_STATIC_BUILD_BUILD_AND_INSTALL_TCL_TK)
build_tcl_unix(${${TCL_SOURCES}_SOURCE_DIR} ${CMAKE_INSTALL_PREFIX})
symlink_tcl(${TCL_STATIC_BUILD_TCLSH_EXE})
build_critcl(${TCL_STATIC_BUILD_UNIX_INSTALLED_TCLSH} ${${CRITCL_SOURCES}_SOURCE_DIR})
build_tcllib(${TCL_STATIC_BUILD_UNIX_INSTALLED_TCLSH} ${${TCLLIB_SOURCES}_SOURCE_DIR} ${CMAKE_INSTALL_PREFIX})
if(TCL_STATIC_BUILD_TK)
build_tk_unix(${${TK_SOURCES}_SOURCE_DIR} ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_PREFIX})
symlink_tk(${TCL_STATIC_BUILD_WISH_EXE})
endif()
endif()
4.12. Windows
4.12.1. Set configure time path to tclsh
Note the “s” after tclsh91 and wish91, probably denotes a static build …
set(TCL_STATIC_BUILD_TCLSH_EXE "tclsh91s.exe")
set(TCL_STATIC_BUILD_WISH_EXE "wish91s.exe")
normalize_path(TCL_STATIC_BUILD_WIN_TCLSH ${FETCHCONTENT_BASE_DIR} "bin" ${TCL_STATIC_BUILD_TCLSH_EXE})
normalize_path(TCL_STATIC_BUILD_WIN_INSTALLED_TCLSH ${CMAKE_INSTALL_PREFIX} "bin" ${TCL_STATIC_BUILD_TCLSH_EXE})
4.12.2. Build Tcl at configure time
if(EXISTS ${TCL_STATIC_BUILD_WIN_TCLSH})
check_tcl_version_matches_required_version(TCL_SOURCES_TCLSH_MATCHES_REQUIRED ${TCL_STATIC_BUILD_WIN_TCLSH})
if(${TCL_SOURCES_TCLSH_MATCHES_REQUIRED})
message(STATUS "The Tcl executable already exists: ${TCL_STATIC_BUILD_WIN_TCLSH}")
else()
message(STATUS "Rebuilding Tcl since the existing executable is not the required version: ${TCL_STATIC_BUILD_VERSION}")
build_tcl_win_at_configure_time(${${TCL_SOURCES}_SOURCE_DIR} ${FETCHCONTENT_BASE_DIR})
endif()
else()
build_tcl_win_at_configure_time(${${TCL_SOURCES}_SOURCE_DIR} ${FETCHCONTENT_BASE_DIR})
endif()
4.12.3. Add Build Time Tcl Target
if(TCL_STATIC_BUILD_BUILD_AND_INSTALL_TCL_TK)
build_tcl_win(${${TCL_SOURCES}_SOURCE_DIR} ${CMAKE_INSTALL_PREFIX})
symlink_tcl_win(${TCL_STATIC_BUILD_TCLSH_EXE})
build_critcl(${TCL_STATIC_BUILD_WIN_INSTALLED_TCLSH} ${${CRITCL_SOURCES}_SOURCE_DIR})
build_tcllib_win(${TCL_STATIC_BUILD_WIN_INSTALLED_TCLSH} ${${TCLLIB_SOURCES}_SOURCE_DIR})
if(TCL_STATIC_BUILD_TK)
build_tk_win(${${TK_SOURCES}_SOURCE_DIR} ${${TCL_SOURCES}_SOURCE_DIR} ${CMAKE_INSTALL_PREFIX})
symlink_tk_win(${TCL_STATIC_BUILD_WISH_EXE})
endif()
endif()
4.13. Utilities
4.13.1. Concatenate and normalize a list of paths
function(normalize_path output_path path)
cmake_path(
APPEND path ${ARGN}
OUTPUT_VARIABLE UNNORMALIZED_PATH_LOCAL
)
cmake_path(
NORMAL_PATH UNNORMALIZED_PATH_LOCAL
OUTPUT_VARIABLE NORMALIZED_PATH_LOCAL
)
set(${output_path} ${NORMALIZED_PATH_LOCAL} PARENT_SCOPE)
endfunction()
4.13.2. Convert path to absolute path
function(to_absolute_path path out_variable)
normalize_path(PATH_LOCAL_NORMALIZED ${path})
file(REAL_PATH ${PATH_LOCAL_NORMALIZED} PATH_LOCAL BASE_DIRECTORY "${CMAKE_BINARY_DIR}")
set(${out_variable} ${PATH_LOCAL} PARENT_SCOPE)
endfunction()
4.13.3. Check Tcl Version
If Tcl has already been built once before check that the version Tcl executable matches what we expect.
function(check_tcl_version_matches_required_version output_variable tclsh)
normalize_path(CHECK_VERSION_TCL_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR} "modules" "check_version.tcl")
execute_process(
COMMAND ${tclsh} ${CHECK_VERSION_TCL_SCRIPT} "${TCL_STATIC_BUILD_VERSION}"
RESULT_VARIABLE TCL_VERSION_CHECK_RESULT_LOCAL
)
if(TCL_VERSION_CHECK_RESULT_LOCAL EQUAL 0)
set(${output_variable} Y PARENT_SCOPE)
else()
set(${output_variable} N PARENT_SCOPE)
endif()
endfunction()
Check the required version against the major/minor/patch version of this Tcl interpreter but only the prefix, eg. 9.1 will match 9.1a
set required_version [lindex $argv 0]
if {[string first $required_version [info patchlevel]] eq 0} {
exit 0
} else {
exit 1
}
4.13.4. Build Tcl at configure time
function(build_tcl_unix_at_configure_time source_dir install_prefix)
find_program(MAKE_EXE NAMES make REQUIRED)
normalize_path(TCL_SOURCES_UNIX_DIRECTORY ${source_dir} "unix")
normalize_path(TCL_SOURCES_UNIX_CONFIGURE ${TCL_SOURCES_UNIX_DIRECTORY} "configure")
execute_process(
COMMAND ${TCL_SOURCES_UNIX_CONFIGURE} --disable-shared --prefix=${install_prefix}
WORKING_DIRECTORY ${TCL_SOURCES_UNIX_DIRECTORY}
COMMAND_ECHO STDOUT
)
execute_process(
COMMAND make install
WORKING_DIRECTORY ${TCL_SOURCES_UNIX_DIRECTORY}
COMMAND_ECHO STDOUT
)
endfunction()
function(build_tcl_win_at_configure_time source_dir install_prefix)
find_program(MAKE_EXE NAMES nmake nmake.exe REQUIRED)
normalize_path(TCL_SOURCES_WIN_DIRECTORY ${source_dir} "win")
execute_process(
COMMAND ${MAKE_EXE} /f makefile.vc release OPTS=static INSTALLDIR=${install_prefix}
WORKING_DIRECTORY ${TCL_SOURCES_WIN_DIRECTORY}
COMMAND_ECHO STDOUT
)
execute_process(
COMMAND ${MAKE_EXE} /f makefile.vc install OPTS=static INSTALLDIR=${install_prefix}
WORKING_DIRECTORY ${TCL_SOURCES_WIN_DIRECTORY}
COMMAND_ECHO STDOUT
)
endfunction()
4.13.5. Build Tcl
function(build_tcl_unix source_dir install_prefix)
find_program(MAKE_EXE make REQUIRED)
normalize_path(TCL_SOURCES_UNIX_DIRECTORY ${source_dir} "unix")
normalize_path(TCL_SOURCES_UNIX_CONFIGURE ${TCL_SOURCES_UNIX_DIRECTORY} "configure")
ExternalProject_Add(
tcl
SOURCE_DIR ${TCL_SOURCES_UNIX_DIRECTORY}
CONFIGURE_COMMAND ${TCL_SOURCES_UNIX_CONFIGURE} --disable-shared --prefix=${install_prefix}
BUILD_COMMAND ${MAKE_EXE}
INSTALL_COMMAND ${MAKE_EXE} install
)
endfunction()
4.13.6. Build Tcl (Windows)
NOTE:
CONFIGURE_COMMANDmust be explicitly set to the empty string on Windows orMSBuild.exewill attempt to buildtclas a CMake projectBUILD_IN_SOURCEtellsMSBuild.exetocdinto thetclWindows source directory, if it isn’t setMSBuild.exewill attempt to build from the top level directory and will error on not being able findmakefile.vc
function(build_tcl_win source_dir install_prefix)
find_program(MAKE_EXE NAMES nmake nmake.exe REQUIRED)
normalize_path(TCL_SOURCES_WIN_DIRECTORY ${source_dir} "win")
ExternalProject_Add(
tcl
SOURCE_DIR ${TCL_SOURCES_WIN_DIRECTORY}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${MAKE_EXE} /f makefile.vc release OPTS=static INSTALLDIR=${install_prefix}
INSTALL_COMMAND ${MAKE_EXE} /f makefile.vc install OPTS=static INSTALLDIR=${install_prefix}
BUILD_IN_SOURCE TRUE
)
endfunction()
4.13.7. Symlink Tcl
function(symlink_tcl tclsh_exe)
add_custom_command(
TARGET tcl
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/bin/${tclsh_exe} ${CMAKE_INSTALL_PREFIX}/bin/tclsh
POST_BUILD
)
endfunction()
4.13.8. Symlink Tcl (Windows)
On Windows symlinking unfortunately requires higher privileges, as a placeholder we just copy the binary as a safe workaround until we come up with a better alternative.
function(symlink_tcl_win tclsh_exe)
add_custom_command(
TARGET tcl
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_INSTALL_PREFIX}/bin/${tclsh_exe} ${CMAKE_INSTALL_PREFIX}/bin/tclsh.exe
POST_BUILD
)
endfunction()
4.13.9. Build Critcl
function(build_critcl tclsh source_dir)
ExternalProject_Add(
critcl
SOURCE_DIR ${source_dir}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMAKE_COMMAND} -E env --modify PATH=path_list_prepend:${CMAKE_INSTALL_PREFIX}/bin ${tclsh} ${source_dir}/build.tcl install
INSTALL_COMMAND ""
BUILD_IN_SOURCE TRUE
DEPENDS tcl
)
endfunction()
4.13.10. Build Tcllib
function(build_tcllib tclsh source_dir install_prefix)
find_program(MAKE_EXE make REQUIRED)
normalize_path(TCLLIB_SOURCES_UNIX_CONFIGURE ${source_dir} "configure")
ExternalProject_Add(
tcllib
SOURCE_DIR ${source_dir}
CONFIGURE_COMMAND ${TCLLIB_SOURCES_UNIX_CONFIGURE} --with-tclsh=${tclsh} --prefix=${install_prefix}
BUILD_COMMAND ${CMAKE_COMMAND} -E env --modify PATH=path_list_prepend:${CMAKE_INSTALL_PREFIX}/bin ${MAKE_EXE}
INSTALL_COMMAND ${CMAKE_COMMAND} -E env --modify PATH=path_list_prepend:${CMAKE_INSTALL_PREFIX}/bin ${MAKE_EXE} install
DEPENDS critcl
)
endfunction()
4.13.11. Build TclLib (Windows)
On Windows, critcl is installed as script, critcl.tcl instead of an executable so we have to set the CRITCL environment variable.
function(build_tcllib_win tclsh source_dir)
ExternalProject_Add(
tcllib
SOURCE_DIR ${source_dir}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMAKE_COMMAND} -E env --modify PATH=path_list_prepend:${CMAKE_INSTALL_PREFIX}/bin ${CMAKE_COMMAND} -E env --modify CRITCL=path_list_prepend:${CMAKE_INSTALL_PREFIX}/bin/critcl.tcl ${tclsh} sak.tcl critcl
INSTALL_COMMAND ${CMAKE_COMMAND} -E env --modify PATH=path_list_prepend:${CMAKE_INSTALL_PREFIX}/bin ${tclsh} installer.tcl
BUILD_IN_SOURCE TRUE
DEPENDS critcl
)
endfunction()
4.13.12. Build Tk
function(build_tk_unix source_dir tcl_install_prefix install_prefix)
find_program(MAKE_EXE make REQUIRED)
normalize_path(TK_SOURCES_UNIX_DIRECTORY ${source_dir} "unix")
normalize_path(TK_SOURCES_UNIX_CONFIGURE ${TK_SOURCES_UNIX_DIRECTORY} "configure")
normalize_path(TCL_LIB_PATH ${tcl_install_prefix} "lib")
ExternalProject_Add(
tk
DEPENDS tcl
SOURCE_DIR ${TK_SOURCES_UNIX_DIRECTORY}
CONFIGURE_COMMAND ${TK_SOURCES_UNIX_CONFIGURE} --disable-shared --with-tcl=${TCL_LIB_PATH} --prefix=${install_prefix}
BUILD_COMMAND ${MAKE_EXE}
INSTALL_COMMAND ${MAKE_EXE} install
)
endfunction()
4.13.13. Build Tk (Windows)
NOTE:
CONFIGURE_COMMANDmust be explicitly set to the empty string on Windows orMSBuild.exewill attempt to buildtkas a CMake projectBUILD_IN_SOURCEtellsMSBuild.exetocdinto thetkWindows source directory, if it isn’t setMSBuild.exewill attempt to build from the top level directory and will error on not being able findmakefile.vc
function(build_tk_win source_dir tcl_sources_dir install_prefix)
find_program(MAKE_EXE NAMES nmake nmake.exe REQUIRED)
normalize_path(TK_SOURCES_WIN_DIRECTORY ${source_dir} "win")
ExternalProject_Add(
tk
DEPENDS tcl
SOURCE_DIR ${TK_SOURCES_WIN_DIRECTORY}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${MAKE_EXE} /f makefile.vc release OPTS=static INSTALLDIR=${install_prefix} TCLDIR=${tcl_sources_dir}
INSTALL_COMMAND ${MAKE_EXE} /f makefile.vc install OPTS=static INSTALLDIR=${install_prefix} TCLDIR=${tcl_sources_dir}
BUILD_IN_SOURCE TRUE
)
endfunction()
4.13.14. Symlink Tk
function(symlink_tk wish_exe)
add_custom_command(
TARGET tk
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/bin/${wish_exe} ${CMAKE_INSTALL_PREFIX}/bin/wish
POST_BUILD
)
endfunction()
4.13.15. Symlink Tk (Windows)
We run into the same issue symlinking Tk as symlinking tclsh, just copy for now and figure it out later …
function(symlink_tk_win wish_exe)
add_custom_command(
TARGET tk
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_INSTALL_PREFIX}/bin/${wish_exe} ${CMAKE_INSTALL_PREFIX}/bin/wish.exe
POST_BUILD
)
endfunction()