Skip to content

Commit

Permalink
Support custom Tcl binary suffix
Browse files Browse the repository at this point in the history
  • Loading branch information
garfieldnate committed Oct 12, 2023
1 parent ed6607d commit 6156bec
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 77 deletions.
2 changes: 1 addition & 1 deletion Core/ClientSMLSWIG/Tcl/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tcl_sml_alias = clone['SML_TCL_ALIAS']

LIB_NAME = 'Tcl_sml_ClientInterface'

if clone.PrepareForCompilingWithTcl(GetOption('tcl')):
if clone.PrepareForCompilingWithTcl(clone["TCL_PATH"], clone["TCL_SUFFIX"]):
print(f'{env["INDENT"]}Tcl SML library is buildable')
else:
print(f'{env["INDENT"]}Tcl SML library is *not* buildable')
Expand Down
7 changes: 5 additions & 2 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ print(f" SWIG: {SML_PYTHON_ALIAS} {SML_TCL_ALIAS} {SML_JAVA_ALIAS
print(f" Extras: debugger* headers* {COMPILE_DB_ALIAS}* tclsoarlib {MSVS_ALIAS} list")
print("Custom Settings available: *default")
print(" Build Type: --dbg, --opt*, --static")
print(" Custom Paths: --out, --build, --tcl")
print(" Custom Paths: --out, --build, --tcl, --tcl-suffix")
print(" Compilation time: --no-svs, --scu*, --no-scu, --no-scu-kernel, --no-scu-cli")
print(" Customizations: --cc, --cxx, --cflags, --lnflags, --no-default-flags, --verbose,")
print("Supported platforms are 64-bit Windows, Linux, and macOS (Intel and ARM)")
Expand Down Expand Up @@ -133,7 +133,8 @@ AddOption('--scu', action='store_true', dest='scu', default=True, help='Build us
AddOption('--out', action='store', type='string', dest='outdir', default=DEF_OUT, nargs=1, metavar='DIR', help='Directory to install binaries. Defaults to "out".')
AddOption('--build', action='store', type='string', dest='build-dir', default=DEF_BUILD, nargs=1, metavar='DIR', help='Directory to store intermediate (object) files. Defaults to "build".')
AddOption('--python', action='store', type='string', dest='python', default=sys.executable, nargs=1, help='Python executable; defaults to same executable used to run SCons')
AddOption('--tcl', action='store', type='string', dest='tcl', nargs=1, help='Active TCL (>= 8.6) libraries')
AddOption('--tcl', action='store', type='string', dest='tcl', nargs=1, help='Path to Tcl installation (ActiveTcl or otherwise)')
AddOption('--tcl-suffix', action='store', type='string', dest='tcl_suffix', default="t", nargs=1, help='Tcl binary suffix (defaults to "t", which is used to indicate full threading support in the standard Tcl build)')
AddOption('--static', action='store_true', dest='static', default=False, help='Use static linking')
AddOption('--dbg', action='store_true', dest='dbg', default=False, help='Enable debug build. Disables compiler optimizations, includes debugging symbols, debug trace statements and assertions')
AddOption('--opt', action='store_false', dest='dbg', default=False, help='Enable optimized build. Enables compiler optimizations, removes debugging symbols, debug trace statements and assertions')
Expand All @@ -149,6 +150,8 @@ env = Environment(
NO_SCU_CLI=GetOption('no_scu_cli'),
BUILD_DIR=GetOption('build-dir'),
OUT_DIR=os.path.realpath(GetOption('outdir')),
TCL_PATH = GetOption('tcl'),
TCL_SUFFIX = GetOption('tcl_suffix'),
SOAR_VERSION=SOAR_VERSION,
VISHIDDEN=False, # needed by swig
JAVAVERSION='11.0',
Expand Down
6 changes: 1 addition & 5 deletions Tcl/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@
# Project: Soar <http://soar.googlecode.com>
# Author: Mazin Assanie

from dataclasses import dataclass
from pathlib import Path
import subprocess
import os
import sys
import SCons.Script

Import('env', 'InstallDir')
clone = env.Clone()

# Set up variables (some are hardcoded for now)
LIB_NAME = 'tclsoarlib'

if clone.PrepareForCompilingWithTcl(GetOption('tcl')):
if clone.PrepareForCompilingWithTcl(clone["TCL_PATH"], clone["TCL_SUFFIX"]):
print(f'{env["INDENT"]}TclSoarLib is buildable')
else:
print(f'{env["INDENT"]}TclSoarLib is *not* buildable')
Expand Down
177 changes: 108 additions & 69 deletions build_support/tcl.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,20 @@ def is_valid(self):
return True, ""


def __get_tcl_from_local_dir_mac(env, local_compiled_dir=None) -> Optional[TclInstallInfo]:
def __get_tcl_from_local_dir_mac(env, local_compiled_dir=None, tcl_suffix=None) -> Optional[TclInstallInfo]:
if not local_compiled_dir:
return None

if tcl_suffix is None:
tcl_suffix = ""

home_dir = Path(local_compiled_dir)
install_info = TclInstallInfo(
home=home_dir,
lib_dir=home_dir / "lib",
include_dir=home_dir / "include",
dyn_lib_name="libtcl8.6.dylib",
include_lib_name="tcl8.6",
dyn_lib_name=f"libtcl8.6{tcl_suffix}.dylib",
include_lib_name=f"tcl8.6{tcl_suffix}",
)
valid, msg = install_info.is_valid()
if not valid:
Expand Down Expand Up @@ -95,10 +98,92 @@ def __get_system_tcl_install_info_mac(env) -> Optional[TclInstallInfo]:
return install_info


def __get_tcl_install_info_mac(env, local_compiled_dir=None) -> Optional[TclInstallInfo]:
return __get_tcl_from_local_dir_mac(env, local_compiled_dir) or \
def __prepare_for_compiling_with_tcl_mac(env, tcl_path_override, tcl_suffix):
install_info = __get_tcl_from_local_dir_mac(env, tcl_path_override, tcl_suffix) or \
__get_brew_tcl_install_info_mac(env) or \
__get_system_tcl_install_info_mac(env)
if not install_info:
return False

print(f"{env['INDENT']}Found Tcl: " + str(install_info.home))

__append_tcl_compile_flags(env, install_info)
if install_info.using_framework:
env.Append(LINKFLAGS=["-framework", install_info.dyn_lib_name])
else:
env.Append(LIBS=[install_info.dyn_lib_name])

# Link error occurs if we include the -bundle flag with -flat_namespace, so we removed it
env.Append(SHLINKFLAGS=env.Split('$LINKFLAGS -flat_namespace -undefined suppress -fmessage-length=0'))

return True


def __prepare_for_compiling_with_tcl_linux(env, tcl_path_override, tcl_suffix):
indent = env["INDENT"]

if tcl_suffix is None:
tcl_suffix = "t"

if tcl_path_override:
home_dir = Path(tcl_path_override)
install_info = TclInstallInfo(
home=home_dir,
lib_dir=home_dir / "lib",
include_dir=home_dir / "include",
dyn_lib_name=f"tcl86{tcl_suffix}.so",
include_lib_name=f"tcl86{tcl_suffix}",
)
valid, msg = install_info.is_valid()
if not valid:
print(f"{indent}Tcl not found in {home_dir}: {msg}")
return False
print(f"{indent}Found Tcl at specified location: " + str(install_info.home))
__append_tcl_compile_flags(env, install_info)
else:
try:
env.ParseConfig("pkg-config tcl --libs --cflags")
except OSError:
print(
f"{indent}pkg-config didn't find tcl package; try `apt-get install tcl-dev`"
)
return False
print(f"{indent}Found Tcl with pkg-config")

return True


def __prepare_for_compiling_with_tcl_windows(env, tcl_path_override, tcl_suffix):
indent = env["INDENT"]

if tcl_suffix is None:
tcl_suffix = "t"

if tcl_path_override:
home_dir = Path(tcl_path_override)
else:
home_dir = Path("C:/ActiveTcl")

install_info = TclInstallInfo(
home=home_dir,
lib_dir=home_dir / "lib",
include_dir=home_dir / "include",
dyn_lib_name=f"tcl86{tcl_suffix}.lib",
include_lib_name=f"tcl86{tcl_suffix}",
)
valid, msg = install_info.is_valid()
if not valid:
print(f"{indent}Tcl not found in {home_dir}: {msg}")
print(f"{indent}{NO_TCL_MSG}")
return False
print(f"{indent}Found Tcl: " + str(install_info.home))

__append_tcl_compile_flags(env, install_info)
# Windows DLLs need to get linked to dependencies, whereas Linux and Mac shared objects do not
# (not sure if this is really needed for TclSoarLib)
env.Append(LIBS=[install_info.include_lib_name])

return True


def __append_tcl_compile_flags(env, install_info: TclInstallInfo):
Expand All @@ -107,88 +192,42 @@ def __append_tcl_compile_flags(env, install_info: TclInstallInfo):
env.Append(LIBPATH=[install_info.lib_dir.absolute()])


def prepare_for_compiling_with_tcl(env, tcl_path_override=None):
# TODO: a more ideal solution would figure out the suffix for you by examining the files in
# the Tcl directory. That's a lot of work for not many users right now; plus, Tcl 8.7 won't
# even have a suffix anymore.
def prepare_for_compiling_with_tcl(env, tcl_path_override=None, tcl_suffix=None):
"""Find the Tcl library and add the necessary compiler/linker flags to env.
Return True if successful, False otherwise.
TODO: tcl_path_override currently ignored on Linux.
tcl_path_override: If specified, this path will be searched first for a Tcl installation.
tcl_suffix: If specified, the suffix will be expected on Tcl binary names. Tcl 8.6 uses
"t" on Linux and Windows and "" on Mac by default. The suffix is supposed to indicate the
type of the Tcl build, but can be overridden by the user when building Tcl.
"""

indent = env["INDENT"]

print("Looking for Tcl...")
if tcl_path_override:
print(f"{env['INDENT']}Tcl path override specified: {tcl_path_override}")
print(f"{indent}Tcl path override specified: {tcl_path_override}")
if not Path(tcl_path_override).exists():
raise FileNotFoundError(
f"Specified Tcl path does not exist: {tcl_path_override}"
)

if sys.platform == "darwin":
install_info = __get_tcl_install_info_mac(env, tcl_path_override)
if not install_info:
print(f"{env['INDENT']}{NO_TCL_MSG}")
if not __prepare_for_compiling_with_tcl_mac(env, tcl_path_override, tcl_suffix):
print(f"{indent}{NO_TCL_MSG}")
return False
print(f"{env['INDENT']}Found Tcl: " + str(install_info.home))

__append_tcl_compile_flags(env, install_info)
if install_info.using_framework:
env.Append(LINKFLAGS=["-framework", install_info.dyn_lib_name])
else:
env.Append(LIBS=[install_info.dyn_lib_name])

# Link error occurs if we include the -bundle flag with -flat_namespace, so we removed it
env.Append(SHLINKFLAGS=env.Split('$LINKFLAGS -flat_namespace -undefined suppress -fmessage-length=0'))

elif sys.platform.startswith("linux"):
if tcl_path_override:
home_dir = Path(tcl_path_override)
install_info = TclInstallInfo(
home=home_dir,
lib_dir=home_dir / "lib",
include_dir=home_dir / "include",
dyn_lib_name="tcl86t.so",
include_lib_name="tcl86t",
)
valid, msg = install_info.is_valid()
if not valid:
print(f"{env['INDENT']}Tcl not found in {home_dir}: {msg}")
print(f"{env['INDENT']}{NO_TCL_MSG}")
return False
print(f"{env['INDENT']}Found Tcl at specified location: " + str(install_info.home))
__append_tcl_compile_flags(env, install_info)
else:
try:
env.ParseConfig("pkg-config tcl --libs --cflags")
except OSError:
print(
f"{env['INDENT']}pkg-config didn't find tcl package; try `apt-get install tcl-dev`"
)
print(f"{env['INDENT']}{NO_TCL_MSG}")
return False
print(f"{env['INDENT']}Found Tcl with pkg-config")
if not __prepare_for_compiling_with_tcl_linux(env, tcl_path_override, tcl_suffix):
print(f"{indent}{NO_TCL_MSG}")
return False

elif sys.platform == "win32":
if tcl_path_override:
home_dir = Path(tcl_path_override)
else:
home_dir = Path("C:/ActiveTcl")

install_info = TclInstallInfo(
home=home_dir,
lib_dir=home_dir / "lib",
include_dir=home_dir / "include",
dyn_lib_name="tcl86t.lib",
include_lib_name="tcl86t",
)
valid, msg = install_info.is_valid()
if not valid:
print(f"{env['INDENT']}Tcl not found in {home_dir}: {msg}")
print(f"{env['INDENT']}{NO_TCL_MSG}")
if not __prepare_for_compiling_with_tcl_windows(env, tcl_path_override, tcl_suffix):
print(f"{indent}{NO_TCL_MSG}")
return False
print(f"{env['INDENT']}Found Tcl: " + str(install_info.home))

__append_tcl_compile_flags(env, install_info)
# Windows DLLs need to get linked to dependencies, whereas Linux and Mac shared objects do not
# (not sure if this is really needed for TclSoarLib)
env.Append(LIBS=[install_info.include_lib_name])

if os.name == "posix":
# -fPic is needed to make the code position independent, which is necessary for Tcl.
Expand Down

0 comments on commit 6156bec

Please sign in to comment.