From: Pat Thoyts Date: Sat, 29 Nov 2008 17:22:48 +0000 (+0000) Subject: GDI+ based element engine for Tk 8.5+ X-Git-Url: https://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;ds=inline;p=gdiplus GDI+ based element engine for Tk 8.5+ --- 8aca5aa4e4c0a725a4503f7e1aeebc9a43dfd71b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d266a83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +nmakehlp.exe +nmakehlp.obj +vercl.[ix] +version.vc +_junk.pch +Release +vc[6789].pdb \ No newline at end of file diff --git a/gdicheckbutton.tcl b/gdicheckbutton.tcl new file mode 100644 index 0000000..fe55f38 --- /dev/null +++ b/gdicheckbutton.tcl @@ -0,0 +1,40 @@ +# Copyright (C) 2008 Pat Thoyts +# +# Example widget definition using the GDI+ element factory +# + +package require Tk 8.5 +package require gdiplusfactory + +# Create a GDI+ indicator element +ttk::style element create gdidot gdiplus { + ellipse -cx 4 -cy 4 -rx 8 -ry 8 +} + +# Create a replacement TCheckbutton layout to use our new element +ttk::style layout Gradient.Checkbutton { + Gradient.Checkbutton.gdidot -side left -sticky {} + Gradient.Checkbutton.focus -side left -sticky w -children { + Gradient.Checkbutton.label -sticky nesw + } +} + +# Specify the state map to change the element gradient fill for different +# widget states. +ttk::style map Gradient.Checkbutton -fill { + disabled {grey80 black} + {pressed !disabled} {black yellow} + + {selected active !disabled} {red white} + {!selected active !disabled} {white red} + + {selected !active !disabled} {black white} + {!selected !active !disabled} {white black} +} + +proc T {} { + variable c + pack [ttk::checkbutton .cn -variable ::c -text Standard] + pack [ttk::checkbutton .cg -variable ::c -text Gradient \ + -style Gradient.Checkbutton] +} \ No newline at end of file diff --git a/gdiplus.cpp b/gdiplus.cpp new file mode 100644 index 0000000..9b02b56 --- /dev/null +++ b/gdiplus.cpp @@ -0,0 +1,465 @@ +/* gdiplus.cpp - Copyright (C) 2008 Pat Thoyts + * + * A GDI+ based element engine for Ttk. + * + */ + +#define STRICT +#define WINVER 0x0500 +#define _WIN32_WINNT 0x0500 +#include +#include +#include +#include + +#ifdef NO_PRIVATE_HEADERS +#include +/* From tkWinInt.h: */ +typedef struct TkWinDCState { + HPALETTE palette; + int bkmode; +} TkWinDCState; + +EXTERN HDC TkWinGetDrawableDC(Display *, Drawable, TkWinDCState*); +EXTERN HDC TkWinReleaseDrawableDC(Drawable, HDC, TkWinDCState*); +#else +#include +#endif + +#define TCL_VERSION_WRONG "8.0" /* Workaround for #1091431 */ + +#if _MSC_VER >= 1000 +#pragma comment(lib,"gdiplus") +#endif + +using namespace Gdiplus; + +/* ---------------------------------------------------------------------- */ + +static RECT +BoxToRect(Ttk_Box b) +{ + RECT rc; + rc.top = b.y; + rc.left = b.x; + rc.bottom = b.y + b.height; + rc.right = b.x + b.width; + return rc; +} + +inline Color +GetXColor(XColor *c, int opacity = 255) +{ + if (c == NULL) + return Color(255, 255, 0, 0); + return Color(opacity, (unsigned char)c->red, + (unsigned char)c->green, (unsigned char)c->blue); +} + +/* ---------------------------------------------------------------------- */ + +typedef enum GdiplusShape { + GdiplusRectangle, GdiplusEllipse +} GdiplusShape; + +static const char *shapeStrings[] = { + "rectangle", "ellipse", NULL +}; + +typedef struct GdiplusElementData { + GdiplusShape shape; + Tcl_Obj *fill; + Tcl_Obj *stroke; + Ttk_Box box; + struct GdiplusElementData *next; +} GdiplusElementData; + +typedef struct GdiplusRectangleData { + GdiplusElementData head; + Rect rc; +} GdiplusRectangleData; + +typedef struct GdiplusEllipseData { + GdiplusElementData head; +} GdiplusEllipseData; + +/* ---------------------------------------------------------------------- */ + +/** + * Obtain a GDI+ brush from a Tcl specification. A simple solid + * colour brush is specified by a single colour. A list with two + * elements will produce a gradient brush. For more complex + * descriptions we will need a specifiction language. For instance, + * SVG defines linearGradient defs with a pair of of points and + * colours at percentages of the region. Also should support path + * gradients. And alpha channel. + */ + +static Brush * +GetBrushFromObj(Tk_Window tkwin, Tcl_Obj *colorObj, const Rect& rc) +{ + Brush *brushPtr; + + if (colorObj == NULL) { + brushPtr = new SolidBrush(Color(255, 255, 255, 255)); + } else { + int objc = 0, n = 0; + Tcl_Obj **objv; + + Tcl_ListObjGetElements(NULL, colorObj, &objc, &objv); + + for (n = 0; n < objc; n++) { + Tk_AllocColorFromObj(NULL, tkwin, objv[n]); + } + + if (objc > 1) { + Color clrA = GetXColor(Tk_GetColorFromObj(tkwin, objv[0])); + Color clrB = GetXColor(Tk_GetColorFromObj(tkwin, objv[1])); + brushPtr = new LinearGradientBrush(Point(rc.X, rc.Y), + Point(rc.X, rc.Y + rc.Height), clrA, clrB); + } else { + Color clr = GetXColor(Tk_GetColorFromObj(tkwin, objv[0]), 220); + brushPtr = new SolidBrush(clr); + } + } + return brushPtr; +} + +/** + * Obtain a GDI+ pen from a Tcl specification. This should use the + * brush specification and add a pen width value as pens can be + * derived from brushes in GDI+. We can for instance have pens based + * upon texture brushes. + */ + +static Pen * +GetPenFromObj(Tk_Window tkwin, Tcl_Obj *colorObj, const Rect &rc) +{ + Pen *penPtr = NULL; + + if (colorObj != NULL) { + int objc = 0, n = 0; + Tcl_Obj **objv; + + Tcl_ListObjGetElements(NULL, colorObj, &objc, &objv); + + for (n = 0; n < objc; n++) { + Tk_AllocColorFromObj(NULL, tkwin, objv[n]); + } + + if (objc > 2) { + Color clrA = GetXColor(Tk_GetColorFromObj(tkwin, objv[0])); + Color clrB = GetXColor(Tk_GetColorFromObj(tkwin, objv[1])); + double penWidth; + Tcl_GetDoubleFromObj(NULL, objv[2], &penWidth); + LinearGradientBrush brush(Point(rc.X, rc.Y), + Point(rc.X, rc.Y + rc.Height), clrA, clrB); + penPtr = new Pen(&brush, (float)penWidth); + } else if (objc == 2) { + Color clr = GetXColor(Tk_GetColorFromObj(tkwin, objv[0])); + double penWidth; + Tcl_GetDoubleFromObj(NULL, objv[1], &penWidth); + penPtr = new Pen(clr, (float)penWidth); + } else { + Color clr = GetXColor(Tk_GetColorFromObj(tkwin, objv[0])); + penPtr = new Pen(clr); + } + } + return penPtr; +} + +/* ---------------------------------------------------------------------- */ + +typedef struct GdiplusElement { + Tcl_Obj *fillObj; + Tcl_Obj *strokeObj; +} GdiplusElement; + +static Ttk_ElementOptionSpec GdiplusElementOptionsSpec[] = +{ + { "-fill", TK_OPTION_ANY, Tk_Offset(GdiplusElement,fillObj), "white" }, + { "-stroke", TK_OPTION_ANY, Tk_Offset(GdiplusElement,strokeObj), "black" }, + {NULL} +}; + +static void +GdiplusElementGeometry(void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + GdiplusElementData *elementPtr = (GdiplusElementData *)clientData; + while (elementPtr != NULL) { + switch (elementPtr->shape) { + case GdiplusRectangle: { + GdiplusRectangleData *rectPtr = (GdiplusRectangleData *)clientData; + *heightPtr = max(*heightPtr, elementPtr->box.height); + *widthPtr = max(*widthPtr, elementPtr->box.width); + break; + } + case GdiplusEllipse: { + GdiplusEllipseData *ellipsePtr = (GdiplusEllipseData *)clientData; + *heightPtr = max(*heightPtr, elementPtr->box.height * 2); + *widthPtr = max(*widthPtr, elementPtr->box.width * 2); + break; + } + } + elementPtr = elementPtr->next; + } +} + +static void +DrawRectangle(GdiplusElement *optionsPtr, Tk_Window tkwin, + Graphics &graphics, GdiplusElementData *elementPtr, Rect rc, int state) +{ + GdiplusRectangleData *rectPtr = (GdiplusRectangleData *)elementPtr; + Tcl_Obj *fillObj = (elementPtr->fill != NULL) ? elementPtr->fill : optionsPtr->fillObj; + Tcl_Obj *strokeObj = (elementPtr->stroke != NULL) ? elementPtr->stroke : optionsPtr->strokeObj; + Brush *brushPtr = GetBrushFromObj(tkwin, fillObj, rc); + Pen *penPtr = GetPenFromObj(tkwin, strokeObj, rc); + graphics.FillRectangle(brushPtr, rc); + if (penPtr) + graphics.DrawRectangle(penPtr, rc); + delete penPtr; + delete brushPtr; +} + +static void +DrawEllipse(GdiplusElement *optionsPtr, Tk_Window tkwin, + Graphics &graphics, GdiplusElementData *elementPtr, Rect rc, int state) +{ + GdiplusEllipseData *ellipsePtr = (GdiplusEllipseData *)elementPtr; + Tcl_Obj *fillObj = (elementPtr->fill != NULL) ? elementPtr->fill : optionsPtr->fillObj; + Tcl_Obj *strokeObj = (elementPtr->stroke != NULL) ? elementPtr->stroke : optionsPtr->strokeObj; + Brush *brushPtr = GetBrushFromObj(tkwin, fillObj, rc); + Pen *penPtr = GetPenFromObj(tkwin, strokeObj, rc); + graphics.FillEllipse(brushPtr, rc); + if (penPtr) + graphics.DrawEllipse(penPtr, rc); + delete penPtr; + delete brushPtr; +} + +static void +GdiplusElementDraw(void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + GdiplusElement *optionsPtr = (GdiplusElement *)elementRecord; + GdiplusElementData *elementPtr = (GdiplusElementData *)clientData; + TkWinDCState dcState; + HDC hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + Graphics graphics(hdc); + graphics.SetSmoothingMode(SmoothingModeHighQuality); + Rect rc(b.x, b.y, b.width - 1, b.height - 1); + + while (elementPtr != NULL) { + switch (elementPtr->shape) { + case GdiplusRectangle: + DrawRectangle(optionsPtr, tkwin, graphics, elementPtr, rc, state); + break; + + case GdiplusEllipse: { + DrawEllipse(optionsPtr, tkwin, graphics, elementPtr, rc, state); + break; + } + } + elementPtr = elementPtr->next; + } + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec GdiplusElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(GdiplusElement), + GdiplusElementOptionsSpec, + GdiplusElementGeometry, + GdiplusElementDraw +}; + +/* ---------------------------------------------------------------------- */ + +static int +ParseShapeDescription(Tcl_Interp *interp, int objc, Tcl_Obj *objv[], + GdiplusElementData **shapesPtrPtr) +{ + GdiplusShape shape; + GdiplusElementData *headPtr = NULL, *tailPtr = NULL, *eltPtr = NULL; + int i = 0; + enum {OPT_X, OPT_Y, OPT_WIDTH, OPT_HEIGHT, OPT_CX, OPT_CY, + OPT_RX, OPT_RY, OPT_FILL, OPT_STROKE }; + const char *rectOptions[] = {"-x", "-y", "-width", "-height", "-cx", "-cy", + "-rx", "-ry", "-fill", "-stroke", NULL }; + const char **optionsPtr; + + while (i < objc) { + + if (Tcl_GetIndexFromObj(interp, objv[i], shapeStrings, + "shape", 0, (int *)&shape) != TCL_OK) { + return TCL_ERROR; + } + ++i; + + switch (shape) { + case GdiplusRectangle: { + GdiplusRectangleData *rectPtr = + (GdiplusRectangleData *)ckalloc(sizeof(GdiplusRectangleData)); + memset(rectPtr, 0, sizeof(GdiplusRectangleData)); + eltPtr = (GdiplusElementData *)rectPtr; + eltPtr->shape = shape; + optionsPtr = rectOptions; + break; + } + case GdiplusEllipse: { + GdiplusEllipseData *ellipsePtr = + (GdiplusEllipseData *)ckalloc(sizeof(GdiplusEllipseData)); + memset(ellipsePtr, 0, sizeof(GdiplusEllipseData)); + eltPtr = (GdiplusElementData *)ellipsePtr; + eltPtr->shape = shape; + optionsPtr = rectOptions; + break; + } + } + + /* process the options */ + while (i < objc) { + const char *optionName = Tcl_GetString(objv[i]); + int option; + + if (optionName[0] != '-') + break; + + OutputDebugString(optionName); + OutputDebugString("\n"); + + if (Tcl_GetIndexFromObj(interp, objv[i], optionsPtr, + "option", 0, &option) != TCL_OK) { + return TCL_ERROR; + } + switch (option) { + case OPT_X: case OPT_CX: { + Tcl_GetIntFromObj(interp, objv[++i], &eltPtr->box.x); + break; + } + case OPT_Y: case OPT_CY: { + Tcl_GetIntFromObj(interp, objv[++i], &eltPtr->box.y); + break; + } + case OPT_WIDTH: case OPT_RX: { + Tcl_GetIntFromObj(interp, objv[++i], &eltPtr->box.width); + break; + } + case OPT_HEIGHT: case OPT_RY: { + Tcl_GetIntFromObj(interp, objv[++i], &eltPtr->box.height); + break; + } + case OPT_STROKE: { + eltPtr->stroke = objv[++i]; + Tcl_IncrRefCount(eltPtr->stroke); + break; + } + case OPT_FILL: { + eltPtr->fill = objv[++i]; + Tcl_IncrRefCount(eltPtr->fill); + break; + } + } + ++i; + } + + if (tailPtr) { + tailPtr->next = eltPtr; + tailPtr = tailPtr->next; + } else { + headPtr = tailPtr = eltPtr; + } + } + + *shapesPtrPtr = headPtr; + return TCL_OK; +} + +static int +CreateGdiplusElement(Tcl_Interp *interp, void *clientData, + Ttk_Theme theme, const char *elementName, int objc, Tcl_Obj *const objv[]) +{ + GdiplusElementData *shapesPtr = NULL; + int eltc, r = TCL_OK; + Tcl_Obj **elts; + + if (objc != 1) { + Tcl_WrongNumArgs(interp, 0, objv, "specification"); + return TCL_ERROR; + } + + if (Tcl_ListObjGetElements(interp, objv[0], &eltc, &elts) != TCL_OK) { + return TCL_ERROR; + } + + r = ParseShapeDescription(interp, eltc, elts, &shapesPtr); + if (r == TCL_OK) { + Ttk_RegisterElement(interp, theme, elementName, + &GdiplusElementSpec, shapesPtr); + Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1)); + } + return r; +} + +/* ---------------------------------------------------------------------- */ + +typedef struct PackageData { + ULONG_PTR token; +} PackageData; + +static void +PackageCleanup(void *clientData) +{ + PackageData *pkgPtr = (PackageData *)clientData; + if (pkgPtr) + GdiplusShutdown(pkgPtr->token); + ckfree((char *)pkgPtr); +} + +EXTERN_C int DLLEXPORT +Gdiplusfactory_Init(Tcl_Interp *interp) +{ + PackageData * pkgPtr; + GdiplusStartupInput gdipsi; + +#ifdef USE_TCL_STUBS + if (Tcl_InitStubs(interp, TCL_VERSION_WRONG, 0) == NULL) { + return TCL_ERROR; + } +#endif +#ifdef USE_TK_STUBS + if (Tk_InitStubs(interp, TK_VERSION, 0) == NULL) { + return TCL_ERROR; + } +#endif +#ifdef USE_TTK_STUBS + if (Ttk_InitStubs(interp) == NULL) { + return TCL_ERROR; + } +#endif + + pkgPtr = (PackageData *)ckalloc(sizeof(PackageData)); + memset(pkgPtr, 0, sizeof(PackageData)); + Ttk_RegisterCleanup(interp, pkgPtr, PackageCleanup); + + Gdiplus::Status status = GdiplusStartup(&pkgPtr->token, &gdipsi, NULL); + if (status != Ok) { + Tcl_SetResult(interp, "failed to initialize GDI+ library", TCL_STATIC); + return TCL_ERROR; + } + + Ttk_RegisterElementFactory(interp, "gdiplus", + CreateGdiplusElement, pkgPtr); + + Tcl_PkgProvide(interp, "gdiplusfactory", PACKAGE_VERSION); + return TCL_OK; +} + +/* + * Local variables: + * mode: c + * indent-tabs-mode: nil + * End: + */ diff --git a/gdiplus.rc b/gdiplus.rc new file mode 100644 index 0000000..d9dc69d --- /dev/null +++ b/gdiplus.rc @@ -0,0 +1,40 @@ +// RCS: @(#) $Id: tile.rc,v 1.3 2006/09/30 17:53:08 jenglish Exp $ +// +// Version Resource Script +// + +#include + +LANGUAGE 0x9, 0x1 /* LANG_ENGLISH, SUBLANG_DEFAULT */ + +VS_VERSION_INFO VERSIONINFO + FILEVERSION COMMAVERSION + PRODUCTVERSION COMMAVERSION + FILEFLAGSMASK 0x3fL +#ifdef DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */ + BEGIN + VALUE "FileDescription", "Ttk GDI+ element factory\0" + VALUE "OriginalFilename", "gdiplus" VERSION ".dll\0" + VALUE "FileVersion", DOTVERSION "\0" + VALUE "LegalCopyright", "Copyright \251 2006 Pat Thoyts\0" + VALUE "ProductName", "Ttk GDI+ element factory\0" + VALUE "ProductVersion", DOTVERSION "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/gditest.tcl b/gditest.tcl new file mode 100644 index 0000000..2f263de --- /dev/null +++ b/gditest.tcl @@ -0,0 +1,79 @@ +# Needs tile loaded (ideally tile with the square widget enabled), then +# load Gdiplus02.dll Gdiplusfactory +# or +#package require ::tile::gdiplus +namespace import -force ::ttk::style + +# +# SVG looks like: +# +# +# + +#rectangle -width 100 -height 100 -fill {red yellow} -stroke {black 1} +#rectangle -width 100 -height 100 -fill {SteelBlue4 LightSteelBlue} -stroke {black 1} + +style element create gdi.gradient gdiplus { + ellipse -cx 50 -cy 50 -rx 30 -ry 30 -fill {#404040 #f0f0f0} +} + +style element create gdi.square gdiplus { + rectangle -width 25 -height 25 -stroke {black 1} +} + +style layout TSample { + Square.gdi.gradient -sticky news + Square.padding -sticky news -children { + Square.label -sticky ew -side left + Square.gdi.square -sticky {} + } +} +style configure TSample -text "This is a GDI+ test" +style configure TSample -fill {#c00000 yellow} +style map TSample -fill { + {disabled} {#202020 #d0d0d0} + {pressed} {yellow #ff0000} + {active !disabled} {#ff0000 yellow} +} + +bind TSample {puts "%W [%W identify %x %y]"} + +proc SampleDragBegin {w x y} { + if {![string equal [$w identify $x $y] "padding"]} { + $w instate !disabled { + $w state pressed + variable $w + array set $w [list x $x y $y] + } + } +} + +proc SampleDragEnd {w x y} { + $w instate {pressed !disabled} { + $w state !pressed + variable $w + if {[info exists $w]} { + set anchor "" + if {$y < ([winfo height $w] / 3)} { set anchor n } + if {$y > ([winfo height $w] / 3) * 2} { set anchor s } + if {$x < ([winfo width $w] / 3)} { append anchor w } + if {$x > ([winfo width $w] / 3) * 2} { append anchor e } + if {[string length $anchor] == 0} { set anchor center } + catch {$w configure -anchor $anchor} + unset $w + } + } +} + +bind TSample {SampleDragBegin %W %x %y} +bind TSample {SampleDragEnd %W %x %y} +bind TSample {%W instate !disabled {%W state active}} +bind TSample {%W state !active} + +if {[info command ::ttk::square] ne {}} { + ttk::square .s -style TSample -class TSample +} else { + ttk::frame .s -style TSample -class TSample +} +pack .s -fill both -expand 1 diff --git a/makefile.vc b/makefile.vc new file mode 100644 index 0000000..1765a00 --- /dev/null +++ b/makefile.vc @@ -0,0 +1,474 @@ +# makefile.vc -- -*- Makefile -*- +# +# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+) +# +# This makefile is based upon the Tcl 8.4 Makefile.vc and modified to +# make it suitable as a general package makefile. Look for the word EDIT +# which marks sections that may need modification. As a minumum you will +# need to change the PROJECT, DOTVERSION and DLLOBJS variables to values +# relevant to your package. +# +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# Copyright (c) 1995-1996 Sun Microsystems, Inc. +# Copyright (c) 1998-2000 Ajuba Solutions. +# Copyright (c) 2001 ActiveState Corporation. +# Copyright (c) 2001-2002 David Gravereaux. +# Copyright (c) 2003-2006 Pat Thoyts +# +#------------------------------------------------------------------------- +# RCS: @(#)$Id: makefile.vc,v 1.9 2007/09/06 21:12:38 patthoyts Exp $ +#------------------------------------------------------------------------- + +# Check to see we are configured to build with MSVC (MSDEVDIR or MSVCDIR) +# or with the MS Platform SDK (MSSDK). Visual Studio .NET 2003 and 2005 define +# VCINSTALLDIR instead. The MSVC Toolkit release defines yet another. +!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(MSSDK) && !defined(VCINSTALLDIR) && !defined(VCToolkitInstallDir) +MSG = ^ +You need to run vcvars32.bat from Developer Studio or setenv.bat from the^ +Platform SDK first to setup the environment. Jump to this line to read^ +the build instructions. +!error $(MSG) +!endif + +#------------------------------------------------------------------------------ +# HOW TO USE this makefile: +# +# 1) It is now necessary to have %MSVCDir% set in the environment. This is +# used as a check to see if vcvars32.bat had been run prior to running +# nmake or during the installation of Microsoft Visual C++, MSVCDir had +# been set globally and the PATH adjusted. Either way is valid. +# +# You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin +# directory to setup the proper environment, if needed, for your current +# setup. This is a needed bootstrap requirement and allows the swapping of +# different environments to be easier. +# +# 2) To use the Platform SDK (not expressly needed), run setenv.bat after +# vcvars32.bat according to the instructions for it. This can also turn on +# the 64-bit compiler, if your SDK has it. +# +# 3) Targets are: +# all -- Builds everything. +# -- Builds the project (eg: nmake sample) +# test -- Builds and runs the test suite. +# install -- Installs the built binaries and libraries to $(INSTALLDIR) +# in an appropriate subdirectory. +# clean/realclean/distclean -- varying levels of cleaning. +# +# 4) Macros usable on the commandline: +# INSTALLDIR= +# Sets where to install Tcl from the built binaries. +# C:\Progra~1\Tcl is assumed when not specified. +# +# OPTS=static,msvcrt,staticpkg,threads,symbols,profile,loimpact,none +# Sets special options for the core. The default is for none. +# Any combination of the above may be used (comma separated). +# 'none' will over-ride everything to nothing. +# +# static = Builds a static library of the core instead of a +# dll. The shell will be static (and large), as well. +# msvcrt = Effects the static option only to switch it from +# using libcmt(d) as the C runtime [by default] to +# msvcrt(d). This is useful for static embedding +# support. +# staticpkg = Effects the static option only to switch +# tclshXX.exe to have the dde and reg extension linked +# inside it. +# nothreads = Turns off multithreading support (not recommended) +# thrdalloc = Use the thread allocator (shared global free pool). +# symbols = Adds symbols for step debugging. +# profile = Adds profiling hooks. Map file is assumed. +# loimpact = Adds a flag for how NT treats the heap to keep memory +# in use, low. This is said to impact alloc performance. +# +# STATS=memdbg,compdbg,none +# Sets optional memory and bytecode compiler debugging code added +# to the core. The default is for none. Any combination of the +# above may be used (comma separated). 'none' will over-ride +# everything to nothing. +# +# memdbg = Enables the debugging memory allocator. +# compdbg = Enables byte compilation logging. +# +# MACHINE=(IX86|IA64|ALPHA|AMD64) +# Set the machine type used for the compiler, linker, and +# resource compiler. This hook is needed to tell the tools +# when alternate platforms are requested. IX86 is the default +# when not specified. If the CPU environment variable has been +# set (ie: recent Platform SDK) then MACHINE is set from CPU. +# +# TMP_DIR= +# OUT_DIR= +# Hooks to allow the intermediate and output directories to be +# changed. $(OUT_DIR) is assumed to be +# $(BINROOT)\(Release|Debug) based on if symbols are requested. +# $(TMP_DIR) will de $(OUT_DIR)\ by default. +# +# TESTPAT= +# Reads the tests requested to be run from this file. +# +# CFG_ENCODING=encoding +# name of encoding for configuration information. Defaults +# to cp1252 +# +# 5) Examples: +# +# Basic syntax of calling nmake looks like this: +# nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]] +# +# Standard (no frills) +# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat +# Setting environment for using Microsoft Visual C++ tools. +# c:\tcl_src\win\>nmake -f makefile.vc all +# c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl +# +# Building for Win64 +# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat +# Setting environment for using Microsoft Visual C++ tools. +# c:\tcl_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL +# Targeting Windows pre64 RETAIL +# c:\tcl_src\win\>nmake -f makefile.vc MACHINE=IA64 +# +#------------------------------------------------------------------------------ +#============================================================================== +############################################################################### +#------------------------------------------------------------------------------ + +!if !exist("makefile.vc") +MSG = ^ +You must run this makefile only from the directory it is in.^ +Please `cd` to its location first. +!error $(MSG) +!endif + +#------------------------------------------------------------------------- +# Project specific information (EDIT) +# +# You should edit this with the name and version of your project. This +# information is used to generate the name of the package library and +# it's install location. +# +# For example, the sample extension is going to build sample04.dll and +# would install it into $(INSTALLDIR)\lib\sample04 +# +# You need to specify the object files that need to be linked into your +# binary here. +# +#------------------------------------------------------------------------- + +PROJECT = Gdiplus + +# Uncomment the following line if this is a Tk extension. +PROJECT_REQUIRES_TK=1 +!include "rules.vc" + +DOTVERSION = 0.2 +VERSION = $(DOTVERSION:.=) +STUBPREFIX = $(PROJECT)stub + +DLLOBJS = \ + $(TMP_DIR)\gdiplus.obj \ + $(TMP_DIR)\ttkStubLib.obj \ +!if !$(STATIC_BUILD) + $(TMP_DIR)\gdiplus.res +!endif + +!if $(TKINSTALL) +MSG=^ +This extension must be built against the Tcl and Tk sources. You must^ +provide TCLDIR and TKDIR on the make command-line. +!error $(MSG) +!endif + +TTKSRCDIR = $(TKDIR)\generic\ttk +TTK_INCLUDES = -I"$(TTKSRCDIR)" + +#------------------------------------------------------------------------- +# Target names and paths ( shouldn't need changing ) +#------------------------------------------------------------------------- + +BINROOT = . +ROOT = . + +PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib +PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT) +PRJLIB = $(OUT_DIR)\$(PRJLIBNAME) + +PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib +PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME) + +### Make sure we use backslash only. +PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION) +LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR) +BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR) +DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR) +SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR) +INCLUDE_INSTALL_DIR = $(_TCLDIR)\include + +### The following paths CANNOT have spaces in them. +GENERICDIR = $(ROOT)\generic +WINDIR = $(ROOT) +LIBDIR = $(ROOT)\library +DOCDIR = $(ROOT)\doc +TOOLSDIR = $(ROOT)\tools +COMPATDIR = $(ROOT)\compat + +#--------------------------------------------------------------------- +# Compile flags +#--------------------------------------------------------------------- + +!if !$(DEBUG) +!if $(OPTIMIZING) +### This cranks the optimization level to maximize speed +cdebug = -Zi $(OPTIMIZATIONS) +!else +cdebug = +!endif +!else if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64" +### Warnings are too many, can't support warnings into errors. +cdebug = -Zi -Od $(DEBUGFLAGS) +!else +cdebug = -Zi -WX $(DEBUGFLAGS) +!endif + +### Declarations common to all compiler options +cwarn = $(WARNINGS) -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE +cflags = -nologo -c $(COMPILERFLAGS) $(cwarn) -Fp$(TMP_DIR)^\ + +!if $(MSVCRT) +!if $(DEBUG) && !$(UNCHECKED) +crt = -MDd +!else +crt = -MD +!endif +!else +!if $(DEBUG) && !$(UNCHECKED) +crt = -MTd +!else +crt = -MT +!endif +!endif + +!if !$(STATIC_BUILD) +cflags = $(cflags) -DUSE_TCL_STUBS +!if defined(TKSTUBLIB) +cflags = $(cflags) -DUSE_TK_STUBS -DUSE_TTK_STUBS +!endif +!endif + +INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" \ + $(TK_INCLUDES) $(TTK_INCLDES) +BASE_CFLAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) +CON_CFLAGS = $(cflags) $(cdebug) $(crt) -DCONSOLE +TCL_CFLAGS = -DPACKAGE_NAME="\"$(PROJECT)\"" \ + -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \ + $(BASE_CFLAGS) $(OPTDEFINES) + +### Stubs files should not be compiled with -GL +STUB_CFLAGS = $(cflags) $(cdebug:-GL=) $(TK_DEFINES) + +#--------------------------------------------------------------------- +# Link flags +#--------------------------------------------------------------------- + +!if $(DEBUG) +ldebug = -debug +!if $(MSVCRT) +ldebug = $(ldebug) -nodefaultlib:msvcrt +!endif +!else +ldebug = -debug -opt:ref -opt:icf,3 +!endif + +### Declarations common to all linker options +lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug) + +!if $(PROFILE) +lflags = $(lflags) -profile +!endif + +!if $(ALIGN98_HACK) && !$(STATIC_BUILD) +### Align sections for PE size savings. +lflags = $(lflags) -opt:nowin98 +!else if !$(ALIGN98_HACK) && $(STATIC_BUILD) +### Align sections for speed in loading by choosing the virtual page size. +lflags = $(lflags) -align:4096 +!endif + +!if $(LOIMPACT) +lflags = $(lflags) -ws:aggressive +!endif + +dlllflags = $(lflags) -dll +conlflags = $(lflags) -subsystem:console +guilflags = $(lflags) -subsystem:windows +!if !$(STATIC_BUILD) +baselibs = $(TCLSTUBLIB) +!if defined(TKSTUBLIB) +baselibs = $(baselibs) $(TKSTUBLIB) +!endif +!endif + +# Avoid 'unresolved external symbol __security_cookie' errors. +# c.f. http://support.microsoft.com/?id=894573 +!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64" +baselibs = $(baselibs) bufferoverflowU.lib +!endif + +#--------------------------------------------------------------------- +# TclTest flags +#--------------------------------------------------------------------- + +!IF "$(TESTPAT)" != "" +TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT) +!ENDIF + +#--------------------------------------------------------------------- +# Project specific targets (EDIT) +#--------------------------------------------------------------------- + +all: setup $(PROJECT) +$(PROJECT): setup $(PRJLIB) pkgIndex +install: install-binaries install-libraries install-docs +pkgIndex: setup $(OUT_DIR)\pkgIndex.tcl + +test: setup $(PROJECT) + @set TCL_LIBRARY=$(TCL_LIBRARY:\=/) + @set TK_LIBRARY=$(TK_LIBRARY:\=/) + @set PATH=$(_TCLBINDIR);$(_TKBINDIR);$(PATH) + @set TCLLIBPATH=$(OUT_DIR:\=/) $(LIBDIR:\=/) + $(DEBUGGER) $(TCLSH) "$(ROOT)/tests/all.tcl" $(TESTFLAGS) + +shell: setup $(PROJECT) + @set TCL_LIBRARY=$(TCL_LIBRARY:\=/) + @set TK_LIBRARY=$(TK_LIBRARY:\=/) + @set PATH=$(_TCLBINDIR);$(_TKBINDIR);$(PATH) + @set TCLLIBPATH=$(OUT_DIR:\=/) $(LIBDIR:\=/) + $(DEBUGGER) $(WISH) + +setup: + @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR) + @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR) + +$(PRJLIB): $(DLLOBJS) +!if $(STATIC_BUILD) + $(lib32) -nologo -out:$@ @<< +$** +<< +!else + $(link32) $(dlllflags) -base:0x10C40000 -out:$@ $(baselibs) @<< +$** +<< + $(_VC_MANIFEST_EMBED_DLL) + -@del $*.exp +!endif + +$(PRJSTUBLIB): $(PRJSTUBOBJS) + $(lib32) -nologo -out:$@ $(PRJSTUBOBJS) + +#--------------------------------------------------------------------- +# Implicit rules +#--------------------------------------------------------------------- + +{$(WINDIR)}.cpp{$(TMP_DIR)}.obj:: + $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< +$< +<< + +{$(WINDIR)}.c{$(TMP_DIR)}.obj:: + $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< +$< +<< + +{$(GENERICDIR)}.c{$(TMP_DIR)}.obj:: + $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< +$< +<< + +{$(COMPATDIR)}.c{$(TMP_DIR)}.obj:: + $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< +$< +<< + +{$(WINDIR)}.rc{$(TMP_DIR)}.res: + $(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \ + -DCOMMAVERSION=$(DOTVERSION:.=,),0,0 \ + -DDOTVERSION=\"$(DOTVERSION)\" \ + -DVERSION=\"$(VERSION)$(SUFX)\" \ +!if $(DEBUG) + -d DEBUG \ +!endif +!if $(TCL_THREADS) + -d TCL_THREADS \ +!endif +!if $(STATIC_BUILD) + -d STATIC_BUILD \ +!endif + $< + +.SUFFIXES: +.SUFFIXES:.c .cpp .rc + +#------------------------------------------------------------------------- +# Explicit dependency rules +# +#------------------------------------------------------------------------- + +$(OUT_DIR)\pkgIndex.tcl: $(ROOT)\pkgIndex.tcl.in + nmakehlp -s << $** > $@ +@PACKAGE_VERSION@ $(DOTVERSION) +@PACKAGE_NAME@ $(PROJECT) +@PKG_LIB_FILE@ $(PRJLIBNAME) +<< + +$(TMP_DIR)\ttkStubLib.obj : $(TTKSRCDIR)\ttkStubLib.c + $(cc32) $(STUB_CFLAGS) $(TCL_INCLUDES) $(TK_INCLUDES) -Zl -DSTATIC_BUILD -Fo$@ $? + +#--------------------------------------------------------------------- +# Installation. (EDIT) +# +# You may need to modify this section to reflect the final distribution +# of your files and possibly to generate documentation. +# +#--------------------------------------------------------------------- + +install-binaries: + @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)' + @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)" + @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL + +### Automatic creation of pkgIndex +#install-libraries: +# @echo Installing library files to '$(SCRIPT_INSTALL_DIR)' +# @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)" +# @echo cd "$(SCRIPT_INSTALL_DIR:\=/)" ; pkg_mkIndex . | $(TCLSH) + +install-libraries: $(OUT_DIR)\pkgIndex.tcl + @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)' + @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)" + @echo Installing package index in '$(SCRIPT_INSTALL_DIR)' + @$(CPY) $(OUT_DIR)\pkgIndex.tcl $(SCRIPT_INSTALL_DIR) + +install-docs: + @echo Installing documentation files to '$(DOC_INSTALL_DIR)' + @if exist $(DOCDIR) $(CPY) $(DOCDIR)\*.n "$(DOC_INSTALL_DIR)" + +#--------------------------------------------------------------------- +# Clean up +#--------------------------------------------------------------------- + +clean: + @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR) + @if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc + @if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i + @if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x + @if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch + +realclean: clean + @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR) + +distclean: realclean + @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe + @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj diff --git a/nmakehlp.c b/nmakehlp.c new file mode 100644 index 0000000..7be773f --- /dev/null +++ b/nmakehlp.c @@ -0,0 +1,726 @@ +/* + * ---------------------------------------------------------------------------- + * nmakehlp.c -- + * + * This is used to fix limitations within nmake and the environment. + * + * Copyright (c) 2002 by David Gravereaux. + * Copyright (c) 2006 by Pat Thoyts + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * ---------------------------------------------------------------------------- + * RCS: @(#) $Id: nmakehlp.c,v 1.21 2007/12/14 02:27:11 patthoyts Exp $ + * ---------------------------------------------------------------------------- + */ + +#define _CRT_SECURE_NO_DEPRECATE +#include +#pragma comment (lib, "user32.lib") +#pragma comment (lib, "kernel32.lib") +#include +#include + +/* + * This library is required for x64 builds with _some_ versions of MSVC + */ +#if defined(_M_IA64) || defined(_M_AMD64) +#if _MSC_VER >= 1400 && _MSC_VER < 1500 +#pragma comment(lib, "bufferoverflowU") +#endif +#endif + +/* ISO hack for dumb VC++ */ +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + + +/* protos */ + +int CheckForCompilerFeature(const char *option); +int CheckForLinkerFeature(const char *option); +int IsIn(const char *string, const char *substring); +int GrepForDefine(const char *file, const char *string); +int SubstituteFile(const char *substs, const char *filename); +const char * GetVersionFromFile(const char *filename, const char *match); +DWORD WINAPI ReadFromPipe(LPVOID args); + +/* globals */ + +#define CHUNK 25 +#define STATICBUFFERSIZE 1000 +typedef struct { + HANDLE pipe; + char buffer[STATICBUFFERSIZE]; +} pipeinfo; + +pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; +pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; + +/* + * exitcodes: 0 == no, 1 == yes, 2 == error + */ + +int +main( + int argc, + char *argv[]) +{ + char msg[300]; + DWORD dwWritten; + int chars; + + /* + * Make sure children (cl.exe and link.exe) are kept quiet. + */ + + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + + /* + * Make sure the compiler and linker aren't effected by the outside world. + */ + + SetEnvironmentVariable("CL", ""); + SetEnvironmentVariable("LINK", ""); + + if (argc > 1 && *argv[1] == '-') { + switch (*(argv[1]+1)) { + case 'c': + if (argc != 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -c \n" + "Tests for whether cl.exe supports an option\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return CheckForCompilerFeature(argv[2]); + case 'l': + if (argc != 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -l \n" + "Tests for whether link.exe supports an option\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return CheckForLinkerFeature(argv[2]); + case 'f': + if (argc == 2) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -f \n" + "Find a substring within another\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } else if (argc == 3) { + /* + * If the string is blank, there is no match. + */ + + return 0; + } else { + return IsIn(argv[2], argv[3]); + } + case 'g': + if (argc == 2) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -g \n" + "grep for a #define\n" + "exitcodes: integer of the found string (no decimals)\n", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return GrepForDefine(argv[2], argv[3]); + case 's': + if (argc == 2) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -s \n" + "Perform a set of string map type substutitions on a file\n" + "exitcodes: 0\n", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return SubstituteFile(argv[2], argv[3]); + case 'V': + if (argc != 4) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -V filename matchstring\n" + "Extract a version from a file:\n" + "eg: pkgIndex.tcl \"package ifneeded http\"", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 0; + } + printf("%s\n", GetVersionFromFile(argv[2], argv[3])); + return 0; + } + } + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -c|-l|-f|-g|-V ...\n" + "This is a little helper app to equalize shell differences between WinNT and\n" + "Win9x and get nmake.exe to accomplish its job.\n", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); + return 2; +} + +int +CheckForCompilerFeature( + const char *option) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sa; + DWORD threadID; + char msg[300]; + BOOL ok; + HANDLE hProcess, h, pipeThreads[2]; + char cmdline[100]; + + hProcess = GetCurrentProcess(); + + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = INVALID_HANDLE_VALUE; + + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = FALSE; + + /* + * Create a non-inheritible pipe. + */ + + CreatePipe(&Out.pipe, &h, &sa, 0); + + /* + * Dupe the write side, make it inheritible, and close the original. + */ + + DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Same as above, but for the error side. + */ + + CreatePipe(&Err.pipe, &h, &sa, 0); + DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Base command line. + */ + + lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch "); + + /* + * Append our option for testing + */ + + lstrcat(cmdline, option); + + /* + * Filename to compile, which exists, but is nothing and empty. + */ + + lstrcat(cmdline, " .\\nul"); + + ok = CreateProcess( + NULL, /* Module name. */ + cmdline, /* Command line. */ + NULL, /* Process handle not inheritable. */ + NULL, /* Thread handle not inheritable. */ + TRUE, /* yes, inherit handles. */ + DETACHED_PROCESS, /* No console for you. */ + NULL, /* Use parent's environment block. */ + NULL, /* Use parent's starting directory. */ + &si, /* Pointer to STARTUPINFO structure. */ + &pi); /* Pointer to PROCESS_INFORMATION structure. */ + + if (!ok) { + DWORD err = GetLastError(); + int chars = snprintf(msg, sizeof(msg) - 1, + "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| + FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], + (300-chars), 0); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg,lstrlen(msg), &err,NULL); + return 2; + } + + /* + * Close our references to the write handles that have now been inherited. + */ + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + WaitForInputIdle(pi.hProcess, 5000); + CloseHandle(pi.hThread); + + /* + * Start the pipe reader threads. + */ + + pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); + pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); + + /* + * Block waiting for the process to end. + */ + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + + /* + * Wait for our pipe to get done reading, should it be a little slow. + */ + + WaitForMultipleObjects(2, pipeThreads, TRUE, 500); + CloseHandle(pipeThreads[0]); + CloseHandle(pipeThreads[1]); + + /* + * Look for the commandline warning code in both streams. + * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002. + */ + + return !(strstr(Out.buffer, "D4002") != NULL + || strstr(Err.buffer, "D4002") != NULL + || strstr(Out.buffer, "D9002") != NULL + || strstr(Err.buffer, "D9002") != NULL + || strstr(Out.buffer, "D2021") != NULL + || strstr(Err.buffer, "D2021") != NULL); +} + +int +CheckForLinkerFeature( + const char *option) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sa; + DWORD threadID; + char msg[300]; + BOOL ok; + HANDLE hProcess, h, pipeThreads[2]; + char cmdline[100]; + + hProcess = GetCurrentProcess(); + + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = INVALID_HANDLE_VALUE; + + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* + * Create a non-inheritible pipe. + */ + + CreatePipe(&Out.pipe, &h, &sa, 0); + + /* + * Dupe the write side, make it inheritible, and close the original. + */ + + DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Same as above, but for the error side. + */ + + CreatePipe(&Err.pipe, &h, &sa, 0); + DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Base command line. + */ + + lstrcpy(cmdline, "link.exe -nologo "); + + /* + * Append our option for testing. + */ + + lstrcat(cmdline, option); + + ok = CreateProcess( + NULL, /* Module name. */ + cmdline, /* Command line. */ + NULL, /* Process handle not inheritable. */ + NULL, /* Thread handle not inheritable. */ + TRUE, /* yes, inherit handles. */ + DETACHED_PROCESS, /* No console for you. */ + NULL, /* Use parent's environment block. */ + NULL, /* Use parent's starting directory. */ + &si, /* Pointer to STARTUPINFO structure. */ + &pi); /* Pointer to PROCESS_INFORMATION structure. */ + + if (!ok) { + DWORD err = GetLastError(); + int chars = snprintf(msg, sizeof(msg) - 1, + "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| + FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], + (300-chars), 0); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg,lstrlen(msg), &err,NULL); + return 2; + } + + /* + * Close our references to the write handles that have now been inherited. + */ + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + WaitForInputIdle(pi.hProcess, 5000); + CloseHandle(pi.hThread); + + /* + * Start the pipe reader threads. + */ + + pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); + pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); + + /* + * Block waiting for the process to end. + */ + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + + /* + * Wait for our pipe to get done reading, should it be a little slow. + */ + + WaitForMultipleObjects(2, pipeThreads, TRUE, 500); + CloseHandle(pipeThreads[0]); + CloseHandle(pipeThreads[1]); + + /* + * Look for the commandline warning code in the stderr stream. + */ + + return !(strstr(Out.buffer, "LNK1117") != NULL || + strstr(Err.buffer, "LNK1117") != NULL || + strstr(Out.buffer, "LNK4044") != NULL || + strstr(Err.buffer, "LNK4044") != NULL); +} + +DWORD WINAPI +ReadFromPipe( + LPVOID args) +{ + pipeinfo *pi = (pipeinfo *) args; + char *lastBuf = pi->buffer; + DWORD dwRead; + BOOL ok; + + again: + if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) { + CloseHandle(pi->pipe); + return (DWORD)-1; + } + ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L); + if (!ok || dwRead == 0) { + CloseHandle(pi->pipe); + return 0; + } + lastBuf += dwRead; + goto again; + + return 0; /* makes the compiler happy */ +} + +int +IsIn( + const char *string, + const char *substring) +{ + return (strstr(string, substring) != NULL); +} + +/* + * Find a specified #define by name. + * + * If the line is '#define TCL_VERSION "8.5"', it returns 85 as the result. + */ + +int +GrepForDefine( + const char *file, + const char *string) +{ + char s1[51], s2[51], s3[51]; + FILE *f = fopen(file, "rt"); + + if (f == NULL) { + return 0; + } + + do { + int r = fscanf(f, "%50s", s1); + + if (r == 1 && !strcmp(s1, "#define")) { + /* + * Get next two words. + */ + + r = fscanf(f, "%50s %50s", s2, s3); + if (r != 2) { + continue; + } + + /* + * Is the first word what we're looking for? + */ + + if (!strcmp(s2, string)) { + double d1; + + fclose(f); + + /* + * Add 1 past first double quote char. "8.5" + */ + + d1 = atof(s3 + 1); /* 8.5 */ + while (floor(d1) != d1) { + d1 *= 10.0; + } + return ((int) d1); /* 85 */ + } + } + } while (!feof(f)); + + fclose(f); + return 0; +} + +/* + * GetVersionFromFile -- + * Looks for a match string in a file and then returns the version + * following the match where a version is anything acceptable to + * package provide or package ifneeded. + */ + +const char * +GetVersionFromFile( + const char *filename, + const char *match) +{ + size_t cbBuffer = 100; + static char szBuffer[100]; + char *szResult = NULL; + FILE *fp = fopen(filename, "rt"); + + if (fp != NULL) { + /* + * Read data until we see our match string. + */ + + while (fgets(szBuffer, cbBuffer, fp) != NULL) { + LPSTR p, q; + + p = strstr(szBuffer, match); + if (p != NULL) { + /* + * Skip to first digit. + */ + + while (*p && !isdigit(*p)) { + ++p; + } + + /* + * Find ending whitespace. + */ + + q = p; + while (*q && (isalnum(*q) || *q == '.')) { + ++q; + } + + memcpy(szBuffer, p, q - p); + szBuffer[q-p] = 0; + szResult = szBuffer; + break; + } + } + fclose(fp); + } + return szResult; +} + +/* + * List helpers for the SubstituteFile function + */ + +typedef struct list_item_t { + struct list_item_t *nextPtr; + char * key; + char * value; +} list_item_t; + +/* insert a list item into the list (list may be null) */ +static list_item_t * +list_insert(list_item_t **listPtrPtr, const char *key, const char *value) +{ + list_item_t *itemPtr = malloc(sizeof(list_item_t)); + if (itemPtr) { + itemPtr->key = strdup(key); + itemPtr->value = strdup(value); + itemPtr->nextPtr = NULL; + + while(*listPtrPtr) { + listPtrPtr = &(*listPtrPtr)->nextPtr; + } + *listPtrPtr = itemPtr; + } + return itemPtr; +} + +static void +list_free(list_item_t **listPtrPtr) +{ + list_item_t *tmpPtr, *listPtr = *listPtrPtr; + while (listPtr) { + tmpPtr = listPtr; + listPtr = listPtr->nextPtr; + free(tmpPtr->key); + free(tmpPtr->value); + free(tmpPtr); + } +} + +/* + * SubstituteFile -- + * As windows doesn't provide anything useful like sed and it's unreliable + * to use the tclsh you are building against (consider x-platform builds - + * eg compiling AMD64 target from IX86) we provide a simple substitution + * option here to handle autoconf style substitutions. + * The substitution file is whitespace and line delimited. The file should + * consist of lines matching the regular expression: + * \s*\S+\s+\S*$ + * + * Usage is something like: + * nmakehlp -S << $** > $@ + * @PACKAGE_NAME@ $(PACKAGE_NAME) + * @PACKAGE_VERSION@ $(PACKAGE_VERSION) + * << + */ + +int +SubstituteFile( + const char *substitutions, + const char *filename) +{ + size_t cbBuffer = 1024; + static char szBuffer[1024], szCopy[1024]; + char *szResult = NULL; + list_item_t *substPtr = NULL; + FILE *fp, *sp; + + fp = fopen(filename, "rt"); + if (fp != NULL) { + + /* + * Build a list of substutitions from the first filename + */ + + sp = fopen(substitutions, "rt"); + if (sp != NULL) { + while (fgets(szBuffer, cbBuffer, sp) != NULL) { + char *ks, *ke, *vs, *ve; + ks = szBuffer; + while (ks && *ks && isspace(*ks)) ++ks; + ke = ks; + while (ke && *ke && !isspace(*ke)) ++ke; + vs = ke; + while (vs && *vs && isspace(*vs)) ++vs; + ve = vs; + while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve; + *ke = 0, *ve = 0; + list_insert(&substPtr, ks, vs); + } + fclose(sp); + } + + /* debug: dump the list */ +#ifdef _DEBUG + { + int n = 0; + list_item_t *p = NULL; + for (p = substPtr; p != NULL; p = p->nextPtr, ++n) { + fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value); + } + } +#endif + + /* + * Run the substitutions over each line of the input + */ + + while (fgets(szBuffer, cbBuffer, fp) != NULL) { + list_item_t *p = NULL; + for (p = substPtr; p != NULL; p = p->nextPtr) { + char *m = strstr(szBuffer, p->key); + if (m) { + char *cp, *op, *sp; + cp = szCopy; + op = szBuffer; + while (op != m) *cp++ = *op++; + sp = p->value; + while (sp && *sp) *cp++ = *sp++; + op += strlen(p->key); + while (*op) *cp++ = *op++; + *cp = 0; + memcpy(szBuffer, szCopy, sizeof(szCopy)); + } + } + printf(szBuffer); + } + + list_free(&substPtr); + } + fclose(fp); + return 0; +} + +/* + * Local variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff --git a/pkgIndex.tcl.in b/pkgIndex.tcl.in new file mode 100644 index 0000000..3e6c392 --- /dev/null +++ b/pkgIndex.tcl.in @@ -0,0 +1 @@ +package ifneeded gdiplusfactory @PACKAGE_VERSION@ [list load [file join $dir @PKG_LIB_FILE@] Gdiplusfactory] \ No newline at end of file diff --git a/rules.vc b/rules.vc new file mode 100644 index 0000000..b740e68 --- /dev/null +++ b/rules.vc @@ -0,0 +1,685 @@ +#------------------------------------------------------------------------------ +# rules.vc -- +# +# Microsoft Visual C++ makefile include for decoding the commandline +# macros. This file does not need editing to build Tcl. +# +# This version is modified from the Tcl source version to support +# building extensions using nmake. +# +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# Copyright (c) 2001-2002 David Gravereaux. +# Copyright (c) 2003-2005 Patrick Thoyts +# +#------------------------------------------------------------------------------ +# RCS: @(#) $Id: rules.vc,v 1.7 2007/09/06 21:12:38 patthoyts Exp $ +#------------------------------------------------------------------------------ + +!ifndef _RULES_VC +_RULES_VC = 1 + +cc32 = $(CC) # built-in default. +link32 = link +lib32 = lib +rc32 = $(RC) # built-in default. + +!ifndef INSTALLDIR +### Assume the normal default. +_INSTALLDIR = C:\Program Files\Tcl +!else +### Fix the path separators. +_INSTALLDIR = $(INSTALLDIR:/=\) +!endif + +!ifndef MACHINE +!if "$(CPU)" == "" || "$(CPU)" == "i386" +MACHINE = IX86 +!else +MACHINE = $(CPU) +!endif +!endif + +!ifndef CFG_ENCODING +CFG_ENCODING = \"cp1252\" +!endif + +#---------------------------------------------------------- +# Set the proper copy method to avoid overwrite questions +# to the user when copying files and selecting the right +# "delete all" method. +#---------------------------------------------------------- + +!if "$(OS)" == "Windows_NT" +RMDIR = rmdir /S /Q +ERRNULL = 2>NUL +!if ![ver | find "4.0" > nul] +CPY = echo y | xcopy /i >NUL +COPY = copy >NUL +!else +CPY = xcopy /i /y >NUL +COPY = copy /y >NUL +!endif +!else # "$(OS)" != "Windows_NT" +CPY = xcopy /i >_JUNK.OUT # On Win98 NUL does not work here. +COPY = copy >_JUNK.OUT # On Win98 NUL does not work here. +RMDIR = deltree /Y +NULL = \NUL # Used in testing directory existence +ERRNULL = >NUL # Win9x shell cannot redirect stderr +!endif +MKDIR = mkdir + +!message =============================================================================== + +#---------------------------------------------------------- +# build the helper app we need to overcome nmake's limiting +# environment. +#---------------------------------------------------------- + +!if !exist(nmakehlp.exe) +!if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul] +!endif +!endif + +#---------------------------------------------------------- +# Test for compiler features +#---------------------------------------------------------- + +### test for optimizations +!if [nmakehlp -c -Ot] +!message *** Compiler has 'Optimizations' +OPTIMIZING = 1 +!else +!message *** Compiler does not have 'Optimizations' +OPTIMIZING = 0 +!endif + +OPTIMIZATIONS = + +!if [nmakehlp -c -Ot] +OPTIMIZATIONS = $(OPTIMIZATIONS) -Ot +!endif + +!if [nmakehlp -c -Oi] +OPTIMIZATIONS = $(OPTIMIZATIONS) -Oi +!endif + +!if [nmakehlp -c -Op] +OPTIMIZATIONS = $(OPTIMIZATIONS) -Op +!endif + +!if [nmakehlp -c -fp:strict] +OPTIMIZATIONS = $(OPTIMIZATIONS) -fp:strict +!endif + +!if [nmakehlp -c -Gs] +OPTIMIZATIONS = $(OPTIMIZATIONS) -Gs +!endif + +!if [nmakehlp -c -GS] +OPTIMIZATIONS = $(OPTIMIZATIONS) -GS +!endif + +!if [nmakehlp -c -GL] +OPTIMIZATIONS = $(OPTIMIZATIONS) -GL +!endif + +DEBUGFLAGS = + +!if [nmakehlp -c -RTC1] +DEBUGFLAGS = $(DEBUGFLAGS) -RTC1 +!elseif [nmakehlp -c -GZ] +DEBUGFLAGS = $(DEBUGFLAGS) -GZ +!endif + +COMPILERFLAGS =-W3 + +# In v13 -GL and -YX are incompatible. +!if [nmakehlp -c -YX] +!if ![nmakehlp -c -GL] +OPTIMIZATIONS = $(OPTIMIZATIONS) -YX +!endif +!endif + +!if "$(MACHINE)" == "IX86" +### test for pentium errata +!if [nmakehlp -c -QI0f] +!message *** Compiler has 'Pentium 0x0f fix' +COMPILERFLAGS = $(COMPILERFLAGSS) -QI0f +!else +!message *** Compiler does not have 'Pentium 0x0f fix' +!endif +!endif + +!if "$(MACHINE)" == "IA64" +### test for Itanium errata +!if [nmakehlp -c -QIA64_Bx] +!message *** Compiler has 'B-stepping errata workarounds' +COMPILERFLAGS = $(COMPILERFLAGS) -QIA64_Bx +!else +!message *** Compiler does not have 'B-stepping errata workarounds' +!endif +!endif + +!if "$(MACHINE)" == "IX86" +### test for -align:4096, when align:512 will do. +!if [nmakehlp -l -opt:nowin98] +!message *** Linker has 'Win98 alignment problem' +ALIGN98_HACK = 1 +!else +!message *** Linker does not have 'Win98 alignment problem' +ALIGN98_HACK = 0 +!endif +!else +ALIGN98_HACK = 0 +!endif + +LINKERFLAGS = + +!if [nmakehlp -l -ltcg] +LINKERFLAGS =-ltcg +!endif + +#---------------------------------------------------------- +# MSVC8 (ships with Visual Studio 2005) generates a manifest +# file that we should link into the binaries. This is how. +#---------------------------------------------------------- + +_VC_MANIFEST_EMBED_EXE= +_VC_MANIFEST_EMBED_DLL= +VCVER=0 +!if ![echo VCVERSION=_MSC_VER > vercl.x] \ + && ![cl -nologo -TC -P vercl.x $(ERRNULL)] +!include vercl.i +!if $(VCVERSION) >= 1500 +VCVER=9 +!elseif $(VCVERSION) >= 1400 +VCVER=8 +!elseif $(VCVERSION) >= 1300 +VCVER=7 +!elseif $(VCVERSION) >= 1200 +VCVER=6 +!endif +!endif + +# Since MSVC8 we must deal with manifest resources. +!if $(VCVERSION) >= 1400 +_VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1 +_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2 +!endif + +#---------------------------------------------------------- +# Decode the options requested. +#---------------------------------------------------------- + +!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"] +STATIC_BUILD = 0 +TCL_THREADS = 1 +DEBUG = 0 +PROFILE = 0 +MSVCRT = 0 +LOIMPACT = 0 +TCL_USE_STATIC_PACKAGES = 0 +USE_THREAD_ALLOC = 1 +USE_THREAD_STORAGE = 1 +UNCHECKED = 0 +!else +!if [nmakehlp -f $(OPTS) "static"] +!message *** Doing static +STATIC_BUILD = 1 +!else +STATIC_BUILD = 0 +!endif +!if [nmakehlp -f $(OPTS) "msvcrt"] +!message *** Doing msvcrt +MSVCRT = 1 +!else +MSVCRT = 0 +!endif +!if [nmakehlp -f $(OPTS) "staticpkg"] +!message *** Doing staticpkg +TCL_USE_STATIC_PACKAGES = 1 +!else +TCL_USE_STATIC_PACKAGES = 0 +!endif +!if [nmakehlp -f $(OPTS) "nothreads"] +!message *** Compile explicitly for non-threaded tcl +TCL_THREADS = 0 +!else +TCL_THREADS = 1 +!endif +!if [nmakehlp -f $(OPTS) "symbols"] +!message *** Doing symbols +DEBUG = 1 +!else +DEBUG = 0 +!endif +!if [nmakehlp -f $(OPTS) "profile"] +!message *** Doing profile +PROFILE = 1 +!else +PROFILE = 0 +!endif +!if [nmakehlp -f $(OPTS) "loimpact"] +!message *** Doing loimpact +LOIMPACT = 1 +!else +LOIMPACT = 0 +!endif +!if [nmakehlp -f $(OPTS) "thrdalloc"] +!message *** Doing thrdalloc +USE_THREAD_ALLOC = 1 +!else +USE_THREAD_ALLOC = 0 +!endif +!if [nmakehlp -f $(OPTS) "thrdstorage"] +!message *** Doing thrdstorage +USE_THREAD_STORAGE = 1 +!else +USE_THREAD_STORAGE = 0 +!endif +!if [nmakehlp -f $(OPTS) "unchecked"] +!message *** Doing unchecked +UNCHECKED = 1 +!else +UNCHECKED = 0 +!endif +!endif + + +!if !$(STATIC_BUILD) +# Make sure we don't build overly fat DLLs. +MSVCRT = 1 +# We shouldn't statically put the extensions inside the shell when dynamic. +TCL_USE_STATIC_PACKAGES = 0 +!endif + + +#---------------------------------------------------------- +# Figure-out how to name our intermediate and output directories. +# We wouldn't want different builds to use the same .obj files +# by accident. +#---------------------------------------------------------- + +#---------------------------------------- +# Naming convention: +# t = full thread support. +# s = static library (as opposed to an +# import library) +# g = linked to the debug enabled C +# run-time. +# x = special static build when it +# links to the dynamic C run-time. +#---------------------------------------- +SUFX = sgx + +!if $(DEBUG) +BUILDDIRTOP = Debug +!else +BUILDDIRTOP = Release +!endif + +!if "$(MACHINE)" != "IX86" +BUILDDIRTOP =$(BUILDDIRTOP)_$(MACHINE) +!endif +!if $(VCVER) > 6 +BUILDDIRTOP =$(BUILDDIRTOP)_VC$(VCVER) +!endif + +!if !$(DEBUG) || $(DEBUG) && $(UNCHECKED) +SUFX = $(SUFX:g=) +!endif + +TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX + +!if !$(STATIC_BUILD) +TMP_DIRFULL = $(TMP_DIRFULL:Static=) +SUFX = $(SUFX:s=) +EXT = dll +!if $(MSVCRT) +TMP_DIRFULL = $(TMP_DIRFULL:X=) +SUFX = $(SUFX:x=) +!endif +!else +TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=) +EXT = lib +!if !$(MSVCRT) +TMP_DIRFULL = $(TMP_DIRFULL:X=) +SUFX = $(SUFX:x=) +!endif +!endif + +!if !$(TCL_THREADS) +TMP_DIRFULL = $(TMP_DIRFULL:Threaded=) +SUFX = $(SUFX:t=) +!endif + +!ifndef TMP_DIR +TMP_DIR = $(TMP_DIRFULL) +!ifndef OUT_DIR +OUT_DIR = .\$(BUILDDIRTOP) +!endif +!else +!ifndef OUT_DIR +OUT_DIR = $(TMP_DIR) +!endif +!endif + + +#---------------------------------------------------------- +# Decode the statistics requested. +#---------------------------------------------------------- + +!if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"] +TCL_MEM_DEBUG = 0 +TCL_COMPILE_DEBUG = 0 +!else +!if [nmakehlp -f $(STATS) "memdbg"] +!message *** Doing memdbg +TCL_MEM_DEBUG = 1 +!else +TCL_MEM_DEBUG = 0 +!endif +!if [nmakehlp -f $(STATS) "compdbg"] +!message *** Doing compdbg +TCL_COMPILE_DEBUG = 1 +!else +TCL_COMPILE_DEBUG = 0 +!endif +!endif + + +#---------------------------------------------------------- +# Decode the checks requested. +#---------------------------------------------------------- + +!if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"] +TCL_NO_DEPRECATED = 0 +WARNINGS = -W3 +!else +!if [nmakehlp -f $(CHECKS) "nodep"] +!message *** Doing nodep check +TCL_NO_DEPRECATED = 1 +!else +TCL_NO_DEPRECATED = 0 +!endif +!if [nmakehlp -f $(CHECKS) "fullwarn"] +!message *** Doing full warnings check +WARNINGS = -W4 +!if [nmakehlp -l -warn:3] +LINKERFLAGS = $(LINKERFLAGS) -warn:3 +!endif +!else +WARNINGS = -W3 +!endif +!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64] +!message *** Doing 64bit portability warnings +WARNINGS = $(WARNINGS) -Wp64 +!endif +!endif + +#---------------------------------------------------------- +# Set our defines now armed with our options. +#---------------------------------------------------------- + +OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) + +!if $(TCL_MEM_DEBUG) +OPTDEFINES = $(OPTDEFINES) -DTCL_MEM_DEBUG +!endif +!if $(TCL_COMPILE_DEBUG) +OPTDEFINES = $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS +!endif +!if $(TCL_THREADS) +OPTDEFINES = $(OPTDEFINES) -DTCL_THREADS=1 +!if $(USE_THREAD_ALLOC) +OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_ALLOC=1 +!endif +!if $(USE_THREAD_STORAGE) +OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_STORAGE=1 +!endif +!endif +!if $(STATIC_BUILD) +OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD +!endif +!if $(TCL_NO_DEPRECATED) +OPTDEFINES = $(OPTDEFINES) -DTCL_NO_DEPRECATED +!endif + +!if $(DEBUG) +OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DEBUG +!elseif $(OPTIMIZING) +OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED +!endif +!if $(PROFILE) +OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED +!endif +!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64" +OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT +!endif + + +#---------------------------------------------------------- +# Locate the Tcl headers to build against +#---------------------------------------------------------- + +!if "$(PROJECT)" == "tcl" + +_TCL_H = ..\generic\tcl.h + +!else + +# If INSTALLDIR set to tcl root dir then reset to the lib dir. +!if exist("$(_INSTALLDIR)\include\tcl.h") +_INSTALLDIR=$(_INSTALLDIR)\lib +!endif + +!if !defined(TCLDIR) +!if exist("$(_INSTALLDIR)\..\include\tcl.h") +TCLINSTALL = 1 +_TCLDIR = $(_INSTALLDIR)\.. +_TCL_H = $(_INSTALLDIR)\..\include\tcl.h +TCLDIR = $(_INSTALLDIR)\.. +!else +MSG=^ +Failed to find tcl.h. Set the TCLDIR macro. +!error $(MSG) +!endif +!else +_TCLDIR = $(TCLDIR:/=\) +!if exist("$(_TCLDIR)\include\tcl.h") +TCLINSTALL = 1 +_TCL_H = $(_TCLDIR)\include\tcl.h +!elseif exist("$(_TCLDIR)\generic\tcl.h") +TCLINSTALL = 0 +_TCL_H = $(_TCLDIR)\generic\tcl.h +!else +MSG =^ +Failed to find tcl.h. The TCLDIR macro does not appear correct. +!error $(MSG) +!endif +!endif +!endif + +#-------------------------------------------------------------- +# Extract various version numbers from tcl headers +# The generated file is then included in the makefile. +#-------------------------------------------------------------- + +!if [echo REM = This file is generated from rules.vc > versions.vc] +!endif +!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \ + && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc] +!endif +!if [echo TCL_MINOR_VERSION = \>> versions.vc] \ + && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc] +!endif +!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \ + && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc] +!endif + +# If building the tcl core then we need additional package versions +!if "$(PROJECT)" == "tcl" +!if [echo PKG_HTTP_VER = \>> versions.vc] \ + && [nmakehlp -V ..\library\http\pkgIndex.tcl http >> versions.vc] +!endif +!if [echo PKG_TCLTEST_VER = \>> versions.vc] \ + && [nmakehlp -V ..\library\tcltest\pkgIndex.tcl tcltest >> versions.vc] +!endif +!if [echo PKG_MSGCAT_VER = \>> versions.vc] \ + && [nmakehlp -V ..\library\msgcat\pkgIndex.tcl msgcat >> versions.vc] +!endif +!if [echo PKG_PLATFORM_VER = \>> versions.vc] \ + && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform " >> versions.vc] +!endif +!if [echo PKG_SHELL_VER = \>> versions.vc] \ + && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform::shell" >> versions.vc] +!endif +!endif + +!include versions.vc + +#-------------------------------------------------------------- +# Setup tcl version dependent stuff headers +#-------------------------------------------------------------- + +!if "$(PROJECT)" != "tcl" + +TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION) + +!if $(TCL_VERSION) < 81 +TCL_DOES_STUBS = 0 +!else +TCL_DOES_STUBS = 1 +!endif + +!if $(TCLINSTALL) +_TCLBINDIR = "$(_TCLDIR)\bin" +TCLSH = "$(_TCLBINDIR)\tclsh$(TCL_VERSION)$(SUFX).exe" +!if !exist($(TCLSH)) && $(TCL_THREADS) +TCLSH = "$(_TCLBINDIR)\tclsh$(TCL_VERSION)t$(SUFX).exe" +!endif +TCLSTUBLIB = "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib" +TCLIMPLIB = "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib" +TCL_LIBRARY = $(_TCLDIR)\lib +TCLREGLIB = "$(_TCLDIR)\lib\tclreg11$(SUFX:t=).lib" +TCLDDELIB = "$(_TCLDIR)\lib\tcldde12$(SUFX:t=).lib" +COFFBASE = \must\have\tcl\sources\to\build\this\target +TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target +TCL_INCLUDES = -I"$(_TCLDIR)\include" +!else +_TCLBINDIR = $(_TCLDIR)\win\$(BUILDDIRTOP) +TCLSH = "$(_TCLBINDIR)\tclsh$(TCL_VERSION)$(SUFX).exe" +!if !exist($(TCLSH)) && $(TCL_THREADS) +TCLSH = "$(_TCLBINDIR)\tclsh$(TCL_VERSION)t$(SUFX).exe" +!endif +TCLSTUBLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib" +TCLIMPLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib" +TCL_LIBRARY = $(_TCLDIR)\library +TCLREGLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg11$(SUFX:t=).lib" +TCLDDELIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde12$(SUFX:t=).lib" +COFFBASE = "$(_TCLDIR)\win\coffbase.txt" +TCLTOOLSDIR = $(_TCLDIR)\tools +TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win" +!endif + +!endif + +#------------------------------------------------------------------------- +# Locate the Tk headers to build against +#------------------------------------------------------------------------- + +!if "$(PROJECT)" == "tk" +_TK_H = ..\generic\tk.h +!endif + +!ifdef PROJECT_REQUIRES_TK +!if !defined(TKDIR) +!if exist("$(_INSTALLDIR)\..\include\tk.h") +TKINSTALL = 1 +_TKDIR = $(_INSTALLDIR)\.. +_TK_H = $(_TKDIR)\include\tk.h +TKDIR = $(_TKDIR) +!elseif exist("$(_TCLDIR)\include\tk.h") +TKINSTALL = 1 +_TKDIR = $(_TCLDIR) +_TK_H = $(_TKDIR)\include\tk.h +TKDIR = $(_TKDIR) +!endif +!else +_TKDIR = $(TKDIR:/=\) +!if exist("$(_TKDIR)\include\tk.h") +TKINSTALL = 1 +_TK_H = $(_TKDIR)\include\tk.h +!elseif exist("$(_TKDIR)\generic\tk.h") +TKINSTALL = 0 +_TK_H = $(_TKDIR)\generic\tk.h +!else +MSG =^ +Failed to find tk.h. The TKDIR macro does not appear correct. +!error $(MSG) +!endif +!endif +!endif + +#------------------------------------------------------------------------- +# Extract Tk version numbers +#------------------------------------------------------------------------- + +!if defined(PROJECT_REQUIRES_TK) || "$(PROJECT)" == "tk" + +!if [echo TK_MAJOR_VERSION = \>> versions.vc] \ + && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc] +!endif +!if [echo TK_MINOR_VERSION = \>> versions.vc] \ + && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc] +!endif +!if [echo TK_PATCH_LEVEL = \>> versions.vc] \ + && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc] +!endif + +!include versions.vc + +TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION) +TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION) + +!if "$(PROJECT)" != "tk" +!if $(TKINSTALL) +_TKBINDIR = $(_TKDIR)\bin +WISH = "$(_TKBINDIR)\wish$(TK_VERSION)$(SUFX).exe" +!if !exist($(WISH)) && $(TCL_THREADS) +WISH = "$(_TKBINDIR)\wish$(TK_VERSION)t$(SUFX).exe" +!endif +TKSTUBLIB = "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib" +TK_LIBRARY = $(_TKDIR)\lib +TKIMPLIB = "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib" +TK_INCLUDES = -I"$(_TKDIR)\include" +!else +_TKBINDIR = $(_TKDIR)\win\$(BUILDDIRTOP) +WISH = "$(_TKBINDIR)\wish$(TCL_VERSION)$(SUFX).exe" +!if !exist($(WISH)) && $(TCL_THREADS) +WISH = "$(_TKBINDIR)\wish$(TCL_VERSION)t$(SUFX).exe" +!endif +TKSTUBLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib" +TKIMPLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib" +TK_LIBRARY = $(_TKDIR)\library +TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib" +!endif +!endif + +!endif + +#---------------------------------------------------------- +# Display stats being used. +#---------------------------------------------------------- + +!message *** Intermediate directory will be '$(TMP_DIR)' +!message *** Output directory will be '$(OUT_DIR)' +!message *** Suffix for binaries will be '$(SUFX)' +!message *** Optional defines are '$(OPTDEFINES)' +!message *** Compiler version $(VCVER). Target machine is $(MACHINE) +!message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)' +!message *** Link options '$(LINKERFLAGS)' + +!endif