From 8aca5aa4e4c0a725a4503f7e1aeebc9a43dfd71b Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Sat, 29 Nov 2008 17:22:48 +0000 Subject: [PATCH] GDI+ based element engine for Tk 8.5+ --- .gitignore | 7 + gdicheckbutton.tcl | 40 +++ gdiplus.cpp | 465 +++++++++++++++++++++++++++++ gdiplus.rc | 40 +++ gditest.tcl | 79 +++++ makefile.vc | 474 +++++++++++++++++++++++++++++ nmakehlp.c | 726 +++++++++++++++++++++++++++++++++++++++++++++ pkgIndex.tcl.in | 1 + rules.vc | 685 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 2517 insertions(+) create mode 100644 .gitignore create mode 100644 gdicheckbutton.tcl create mode 100644 gdiplus.cpp create mode 100644 gdiplus.rc create mode 100644 gditest.tcl create mode 100644 makefile.vc create mode 100644 nmakehlp.c create mode 100644 pkgIndex.tcl.in create mode 100644 rules.vc 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 -- 2.23.0