CC.tcl is a compiler driver derived from critcl which is intended to convert C.tcl's output into loadable extensions. There is code here for cross-compilation, but that is completely untested. All I can claim is it works on my machine.
# CC - compile C code intertexted in Tcl
#
# slavishly derived and simplified from the excellent critcl
# Colin McCormack, Steve Landers, Jean-Claude Wippler other parties
package require platform
package provide CC 1.0
# CC - compiler driver
namespace eval CC {
proc param {name content} {
variable param
foreach line [split $content \n] {
set line [string trim $line]
if {![regexp {(\S+)\s+(.*)} $line -> n v]} {
dict set param $name $line ""
} else {
dict set param $name $n $v
}
}
}
# platform sets the platform (defaults to platform::generic)
# compile compile a C source file to an object file
# version print the compiler version number
# link link one or more object files and create a shared library
# preproc_define preprocess C source file (for critcl::cdefines)
# preproc_enum ditto
# tclstubs cflag to set USE_TCL_STUBS
# tkstubs cflag to set USE_TK_STUBS
# debug_memory cflag to enable memory debugging
# debug_symbols cflag to add symbols to resulting library
# object file extension for object files
# output flag to set output file
# strip cflag to tell linker to strip symbols
# optimize cflag to specify optimization level
# include cflag to add an include directory
# noassert cflag to turn off assertions in Tcl code
# threadflags cflags to enable threaded build
# target indicates that this is a cross-compile target
# sharedlibext the platform shared library extension
#
# (Support for Fortran)
# fcompile compile a Fortran source file to an object file
# fversion print the Fortran compiler version number
# flink link one or more object files and create a shared library,
# if at least one object file comes from Fortran
# foutput Fortran flag to set output file
# finclude Fortran flag to add an include directory
# fextra_cflags Extra C flags for indicating type of Fortran compiler
param * {
compile gcc -c -fPIC
version gcc -v
link gcc -shared -Wl,--export-dynamic -fPIC
include -I
preproc_define gcc -E -dM
preproc_enum gcc -E
tclstubs -DUSE_TCL_STUBS
tkstubs -DUSE_TK_STUBS
debug_memory -DTCL_MEM_DEBUG
debug_symbols -ggdb3
object .o
output -o %OUTFILE%
ldoutput
link_debug -ggdb3
link_release
link_preload --unresolved-symbols=ignore-in-shared-libs
_strip -Wl,-s
strip
optimize -O2
noassert -DNDEBUG
threadflags -DUSE_THREAD_ALLOC=1 -D_REENTRANT=1 -D_THREAD_SAFE=1 -DHAVE_PTHREAD_ATTR_SETSTACKSIZE=1 -DHAVE_READDIR_R=1 -DTCL_THREADS=1
}
param linux-* {
sharedlibext .so
}
# default on OSX ppc is universal containing ppc and x86 32 bit
param macosx-powerpc {
compile gcc -c -arch ppc
link gcc -bundle -arch ppc
link_preload -undefined dynamic_lookup -mmacosx-version-min=10.3
strip
}
# default on OSX intel is universal containing x86 32 and 64 bit
param macosx-ix86 {
compile gcc -c -arch i386 -arch x86_64
link gcc -bundle -arch i386 -arch x86_64
link_preload -undefined dynamic_lookup -mmacosx-version-min=10.3
strip
}
# target for most common macosx architectures
param macosx-most {
compile gcc -c -arch i386 -arch x86_64 -arch ppc
link gcc -bundle -arch i386 -arch x86_64 -arch ppc
link_preload -undefined dynamic_lookup -mmacosx-version-min=10.3
strip
}
# target for all macosx architectures
param macosx-all {
compile gcc -c -arch i386 -arch x86_64 -arch ppc -arch ppc64
link gcc -bundle -arch i386 -arch x86_64 -arch ppc -arch ppc64
link_preload -undefined dynamic_lookup -mmacosx-version-min=10.3
strip
}
# OSX ppc 32 bit
param macosx-ppc32 {
compile gcc -c -arch ppc
link gcc -bundle -arch ppc
link_preload -undefined dynamic_lookup
strip
}
# OSX ppc 64 bit
param macosx-ppc64 {
compile gcc -c -arch ppc64
link gcc -bundle -arch ppc64
link_preload -undefined dynamic_lookup
strip
}
# OSX x86 32 bit
param macosx-x86_32 {
compile gcc -c -arch i386
link gcc -bundle -arch i386
link_preload -undefined dynamic_lookup
strip
}
# OSX x86 64 bit
param macosx-x86_64 {
compile gcc -c -arch x86_64
link gcc -bundle -arch x86_64
link_preload -undefined dynamic_lookup
strip
}
# Linux - 32 bit or 64 bit build - select using "-target" if you don't want
# the platform default (32 on 32, 64 on 64)
param linux-32-* {
compile gcc -c -m32
}
param linux-64-* {
compile gcc -c -m64
}
# Windows - using MSVC++ 6.0
#
# Note: the language option for cl is -TC for c and -TP for c++ or
# it can treat single files -Tc<filename>
#
param win32-ix86-cl {
compile cl -nologo -c
link link -nologo
preproc_define cl -nologo -E
preproc_enum cl -nologo -E
object .obj
debug_symbols -W3 -Od -Zi -GZ -MDd -D_DEBUG
optimize -W3 -O2 -Op -Gs -MD
output -Fo%OUTFILE%
ldoutput -dll -out:%OUTFILE%
link_debug -debug:full -debugtype:cv
link_release -release -opt:ref -opt:icf,3 -ws:aggressive
link_preload
strip
version cl
}
# Cross-compile for Windows using Xmingwin
param mingw32 {
compile gcc -c -nostdlib
link gcc -shared
link_preload
sharedlibext .dll
tcl_platform(byteOrder) littleEndian
tcl_platform(machine) intel
tcl_platform(os) Windows NT
tcl_platform(osVersion) 5.0
tcl_platform(platform) windows
tcl_platform(wordSize) 4
}
# Cross-compile for ARM (n770/Zaurus/etc) using Scratchbox et al
param linux-arm {
tcl_platform(byteOrder) littleEndian
tcl_platform(machine) arm
tcl_platform(os) Linux
tcl_platform(osVersion) 2.6
tcl_platform(platform) unix
tcl_platform(wordSize) 4
}
# hpux itanium, native cc, 32 and 64bit builds.
# +z <=> -fPIC on hpux.
# +DD64 invokes the 64bit mode.
param hpux-ia64_32 {
compile cc -c +z
link ld -b
preproc_define cc -E
preproc_enum cc -E
}
param hpux-ia64 {
compile cc -c +z +DD64
link ld -b
preproc_define cc -E
preproc_enum cc -E
}
# hpux, itanium, gcc
# This works only if the -lgcc for 64bit is somewhere reachable.
# hpux-ia64 gcc -c -fPIC -mlp64
# aix, rs6000/powerpc, native cc, 32bit build
# The link line is pretty much copied from Tcl.
# NOTE: ldAix was copied from Tcl into a directory in the PATH.
# It might make sense to stuff this file into critcl and then copy it out when
# needed, either into a fixed place, or tempdir. In the latter case the link
# line needs some way of getting the value substituted into it. I have no
# idea of the critcl config allows that, and if yes, nor how.
# cc_r = something with thread-enabled. better use it than cc and have things
# fail.
param aix-powerpc {
compile cc_r -c -O
link ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry -lm -lc
preproc_define cc -E
preproc_enum cc -E
}
variable preload {} ;# libs to preload
variable libfile {} ;# libs
proc compile {ns args} {
set copts ""
set cflags ""
set libs ""
set ldflags ""
if {![info exists platform] || $platform eq ""} {
set platform [::platform::generic]
}
# find matching parameter sets
variable param
set matches {}
dict for {n v} $param {
#puts stderr "pmatch $n $platform: [string match $n $platform]"
if {[string match $n $platform]} {
lappend matches [string map {* \xff} $n]
}
}
# roughly sort the matches into ascending order
set matches [lsort -dictionary $matches]
set matches [string map {\xff *} $matches]
# combine matching parameter sets
set p {}
foreach n $matches {
set p [dict merge $p [dict get $param $n]]
}
# get the C source and populate the command line
set source [$ns generate]
set sfd [file tempfile src]; puts $sfd $source; close $sfd
set obj $src; file rename $src $src.c; append src .c
dict with p {
set cmdline "$compile $cflags $threadflags $tclstubs $copts"
set obj $obj$object
append cmdline " " [string map [list %OUTFILE% $obj] $output]
# add the Tk stuff
if {[dict exists $args tk] && [dict get $args tk]} {
append cmdline " $tkstubs"
}
if {![dict exists $args optimize] || ![dict get $args optimize]} {
append cmdline " $optimize"
}
if {![dict exists $args symbols] || [dict get $args symbols]} {
append cmdline " $debug_symbols"
}
append cmdline " " [list $src] ;# record the source file name in the cmdline
}
puts stderr "Compiling: $cmdline"
if {[catch {
exec {*}$cmdline
} result eo]} {
error "Compilation '$cmdline' failed: '$result' ($eo)"
}
# link the resultant .o file into an .so file
lappend objs $obj
set target [$ns package]
set libs {-L/usr/lib -ltclstub8.5}
# -ltcl8.6
dict with p {
set cmdline $link
variable preload
if {[llength $preload]} {
append cmdline " $link_preload"
}
set outfile $target$sharedlibext
set ldout [string map [list %OUTFILE% $outfile] $ldoutput]
if {$ldout eq ""} {
set ldout [string map [list %OUTFILE% $outfile] $output]
}
if {![dict exists $args symbols] || [dict get $args symbols]} {
append cmdline " $link_debug $ldout"
} else {
append cmdline " $strip $link_release $ldout"
}
variable libfile
append cmdline " " $libfile
append cmdline " " $objs
append cmdline " $libs $ldflags"
}
puts stderr "linking: $cmdline"
if {[catch {
exec {*}$cmdline
} result eo]} {
error "Linking '$cmdline' failed: '$result' ($eo)"
}
return $obj
}
::namespace export -clear *
::namespace ensemble create -subcommands {}
}
if {[info exists argv0] && ($argv0 eq [info script])} {
package require C
C proc add {int x int y} int {
return x + y;
}
C proc cube {int x} int {
return x * x * x;
}
C package Junk
puts [CC compile [C namespace]]
load Junk.so
puts [add 345 123]
puts [cube 99]
}AMG: To get this to work with Tcl 8.6 CVS HEAd compiled with --enable-symbols in MinGW, I had to make the following changes:
I don't know the "right" way to make any of these changes; obviously my patched CC.tcl will only work in my exact configuration. I tried adding new "param win32" and "param win32-ix86" blocks, but -fPIC still got passed to the compiler.
Also. What, if anything, has to be done to get Tk support?