Index: undroid/DiffUtilTcl/ChangeLog ================================================================== --- undroid/DiffUtilTcl/ChangeLog +++ undroid/DiffUtilTcl/ChangeLog @@ -1,5 +1,15 @@ +2019-03-27 Peter Spjuth + Always allow post processing for small blocks. + +2018-10-07 Peter Spjuth + Bumped revision to 0.4.1 + Working compareStreams command. + +2018-10-06 Peter Spjuth + Started on compareStreams command. + 2017-12-02 Peter Spjuth Bumped revision to 0.4.0 Require Tcl 8.6. 2017-12-02 Peter Spjuth @@ -57,11 +67,11 @@ Lots of restructuring to allow LcsCore to work recursively. Specially, all lines are included in V/E/P vectors even if a range is set. 2012-07-02 Peter Spjuth - Handle alignment withing postprocessing of forbidden matches. + Handle alignment within postprocessing of forbidden matches. 2012-06-26 Peter Spjuth Bug-fix in postprocessing of forbidden lines. 2012-06-26 Peter Spjuth Index: undroid/DiffUtilTcl/RevisionBump.txt ================================================================== --- undroid/DiffUtilTcl/RevisionBump.txt +++ undroid/DiffUtilTcl/RevisionBump.txt @@ -1,11 +1,10 @@ The following files contain current revision and needs to be changed when bumping revisions: configure.ac (AC_INIT) doc/diffutil.man (two places) tcl/diffutil.tcl (package provide) -win/makefile.vc (DOTVERSION) Then rerun autoconf configure make clean Index: undroid/DiffUtilTcl/configure ================================================================== --- undroid/DiffUtilTcl/configure +++ undroid/DiffUtilTcl/configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.63 for DiffUtil 0.4.0. +# Generated by GNU Autoconf 2.63 for DiffUtil 0.4.1. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. @@ -592,12 +592,12 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='DiffUtil' PACKAGE_TARNAME='diffutil' -PACKAGE_VERSION='0.4.0' -PACKAGE_STRING='DiffUtil 0.4.0' +PACKAGE_VERSION='0.4.1' +PACKAGE_STRING='DiffUtil 0.4.1' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include @@ -1317,11 +1317,11 @@ # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures DiffUtil 0.4.0 to adapt to many kinds of systems. +\`configure' configures DiffUtil 0.4.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. @@ -1378,11 +1378,11 @@ _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of DiffUtil 0.4.0:";; + short | recursive ) echo "Configuration of DiffUtil 0.4.1:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options @@ -1477,11 +1477,11 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -DiffUtil configure 0.4.0 +DiffUtil configure 0.4.1 generated by GNU Autoconf 2.63 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation @@ -1491,11 +1491,11 @@ fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by DiffUtil $as_me 0.4.0, which was +It was created by DiffUtil $as_me 0.4.1, which was generated by GNU Autoconf 2.63. Invocation command line was $ $0 $@ _ACEOF @@ -12522,11 +12522,11 @@ # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by DiffUtil $as_me 0.4.0, which was +This file was extended by DiffUtil $as_me 0.4.1, which was generated by GNU Autoconf 2.63. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS @@ -12572,11 +12572,11 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -DiffUtil config.status 0.4.0 +DiffUtil config.status 0.4.1 configured by $0, generated by GNU Autoconf 2.63, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation Index: undroid/DiffUtilTcl/configure.ac ================================================================== --- undroid/DiffUtilTcl/configure.ac +++ undroid/DiffUtilTcl/configure.ac @@ -17,11 +17,11 @@ # so you can encode the package version directly into the source files. # This will also define a special symbol for Windows (BUILD_ # so that we create the export library with the dll. #----------------------------------------------------------------------- -AC_INIT([DiffUtil], [0.4.0]) +AC_INIT([DiffUtil], [0.4.1]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable == "unix" or "windows" # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. Index: undroid/DiffUtilTcl/doc/diffutil.html ================================================================== --- undroid/DiffUtilTcl/doc/diffutil.html +++ undroid/DiffUtilTcl/doc/diffutil.html @@ -1,6 +1,6 @@ - + DiffUtil - Comparision Utilities - - - -
-

DiffUtil(n) 0.4.0 diffutil "Comparision Utilities"

-

Name

-

DiffUtil - Compare Stuff

-
- -

Synopsis

-
-
    -
  • package require Tcl 8.6
  • -
  • package require DiffUtil ?0.4.0?
  • -
- -
-
-

Description

+ + + +
+

DiffUtil(n) 0.4.1 diffutil "Comparision Utilities"

+

Name

+

DiffUtil - Compare Stuff

+
+ + +

Description

This package provides utilites for comparisons of strings, lists and files. The base comparison is a Longest Common Substring algorithm based on J. W. Hunt and M. D. McIlroy, "An algorithm for differential file comparison," Comp. Sci. Tech. Rep. #41, Bell Telephone Laboratories (1976). Available on the Web at the second author's personal site: http://www.cs.dartmouth.edu/~doug/

-

COMMANDS

-
+

COMMANDS

+
::DiffUtil::diffFiles ?options? file1 file2

Compare two files line by line. The return value depends on the -result option, see below.

-
+
-nocase

Ignore case.

-i

Ignore case.

-b
@@ -178,11 +180,11 @@

Like -regsub but only applied to the first file.

-regsubright list

Like -regsub but only applied to the second file.

-result style

Select result style. The default is diff.

-
+
diff

Returns a list of differences, each in a four element list. {LineNumber1 NumberOfLines1 LineNumber2 NumberOfLines2} The first line in a file is number 1.

match
@@ -199,11 +201,11 @@

Apply gunzip decompression when reading files. Requires zlib.

::DiffUtil::diffLists ?options? list1 list2

Compare two lists element by element. The return value depends on the -result option, see below.

-
+
-nocase

Ignore case.

-i

Ignore case.

-b
@@ -219,11 +221,11 @@ step, but empty elements within change blocks will be reported as changes.

-nodigit

Consider any sequence of digits equal.

-result style

Select result style. The default is diff.

-
+
diff

Returns a list of differences, each in a four element list. {ElementIndex1 NumberOfElements1 ElementIndex2 NumberOfElements2}

match

The return value is a list of two lists of equal length. The first @@ -230,20 +232,45 @@ sublist is of indices in list1, and the second sublist is of indices in list2. Each corresponding pair of indices corresponds to equal elements in the sequences.

+
::DiffUtil::compareFiles ?options? file1 file2
+

Compare two files. +The return value is a boolean which is true when equal.

+
+
-nocase
+

Ignore case.

+
-ignorekey
+

Ignore keyword substitutions. This is limited to the first 60k of the file.

+
-encoding enc
+

Read files with this encoding. (As in fconfigure -encoding.)

+
-translation trans
+

Read files with this translation. (As in fconfigure -translation.)

+
+
::DiffUtil::compareStreams ?options? ch1 ch2
+

Compare two channel streams. +The return value is a boolean which is true when equal.

+
+
-nocase
+

Ignore case.

+
-ignorekey
+

Ignore keyword substitutions. This is limited to the first 60k read.

+
-binary
+

Treat stream as binary data. Normally this means it is configured +with -translation binary.

+
-

EXAMPLES

-
+

EXAMPLES

+
 % DiffUtil::diffFiles $file1 $file2
 {{3 2 3 4}}
 
-

Keywords

+

Keywords

diff, lcs, longest common substring

- Index: undroid/DiffUtilTcl/doc/diffutil.man ================================================================== --- undroid/DiffUtilTcl/doc/diffutil.man +++ undroid/DiffUtilTcl/doc/diffutil.man @@ -1,11 +1,11 @@ -[manpage_begin DiffUtil n 0.4.0] +[manpage_begin DiffUtil n 0.4.1] [copyright {2010, Peter Spjuth}] [moddesc {Comparision Utilities}] [titledesc {Compare Stuff}] [require Tcl 8.6] -[require DiffUtil [opt 0.4.0]] +[require DiffUtil [opt 0.4.1]] [description] [para] This package provides utilites for comparisons of strings, lists and files. @@ -23,11 +23,11 @@ [opt [arg options]] [arg file1] [arg file2]] Compare two files line by line. The return value depends on the [arg -result] option, see below. -[list_begin opt] +[list_begin options] [opt_def -nocase] Ignore case. [opt_def -i] @@ -104,11 +104,11 @@ [opt [arg options]] [arg list1] [arg list2]] Compare two lists element by element. The return value depends on the [arg -result] option, see below. -[list_begin opt] +[list_begin options] [opt_def -nocase] Ignore case. [opt_def -i] @@ -142,10 +142,52 @@ sublist is of indices in [arg list1], and the second sublist is of indices in [arg list2]. Each corresponding pair of indices corresponds to equal elements in the sequences. [list_end] +[list_end] + +[call [cmd "::DiffUtil::compareFiles"] \ + [opt [arg options]] [arg file1] [arg file2]] + +Compare two files. +The return value is a boolean which is true when equal. + +[list_begin options] + +[opt_def -nocase] +Ignore case. + +[opt_def -ignorekey] +Ignore keyword substitutions. This is limited to the first 60k of the file. + +[opt_def -encoding [arg enc]] +Read files with this encoding. (As in fconfigure -encoding.) + +[opt_def -translation [arg trans]] +Read files with this translation. (As in fconfigure -translation.) + +[list_end] + +[call [cmd "::DiffUtil::compareStreams"] \ + [opt [arg options]] [arg ch1] [arg ch2]] + +Compare two channel streams. +The return value is a boolean which is true when equal. + +[list_begin options] + +[opt_def -nocase] +Ignore case. + +[opt_def -ignorekey] +Ignore keyword substitutions. This is limited to the first 60k read. + +[opt_def -binary] +Treat stream as binary data. Normally this means it is configured +with -translation binary. + [list_end] [list_end] [section EXAMPLES] Index: undroid/DiffUtilTcl/doc/diffutil.n ================================================================== --- undroid/DiffUtilTcl/doc/diffutil.n +++ undroid/DiffUtilTcl/doc/diffutil.n @@ -1,10 +1,10 @@ '\" '\" Generated from file 'diffutil\&.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2010, Peter Spjuth '\" -.TH "DiffUtil" n 0\&.4\&.0 diffutil "Comparision Utilities" +.TH "DiffUtil" n 0\&.4\&.1 diffutil "Comparision Utilities" .\" The -*- nroff -*- definitions below are for supplemental macros used .\" in Tcl/Tk manual entries. .\" .\" .AP type name in/out ?indent? .\" Start paragraph describing an argument to a library procedure. @@ -274,15 +274,19 @@ .SH NAME DiffUtil \- Compare Stuff .SH SYNOPSIS package require \fBTcl 8\&.6\fR .sp -package require \fBDiffUtil ?0\&.4\&.0?\fR +package require \fBDiffUtil ?0\&.4\&.1?\fR .sp \fB::DiffUtil::diffFiles\fR ?\fIoptions\fR? \fIfile1\fR \fIfile2\fR .sp \fB::DiffUtil::diffLists\fR ?\fIoptions\fR? \fIlist1\fR \fIlist2\fR +.sp +\fB::DiffUtil::compareFiles\fR ?\fIoptions\fR? \fIfile1\fR \fIfile2\fR +.sp +\fB::DiffUtil::compareStreams\fR ?\fIoptions\fR? \fIch1\fR \fIch2\fR .sp .BE .SH DESCRIPTION .PP This package provides utilites for comparisons of strings, lists @@ -416,10 +420,44 @@ sublist is of indices in \fIlist1\fR, and the second sublist is of indices in \fIlist2\fR\&. Each corresponding pair of indices corresponds to equal elements in the sequences\&. .RE .RE +.TP +\fB::DiffUtil::compareFiles\fR ?\fIoptions\fR? \fIfile1\fR \fIfile2\fR +Compare two files\&. +The return value is a boolean which is true when equal\&. +.RS +.TP +\fB-nocase\fR +Ignore case\&. +.TP +\fB-ignorekey\fR +Ignore keyword substitutions\&. This is limited to the first 60k of the file\&. +.TP +\fB-encoding\fR \fIenc\fR +Read files with this encoding\&. (As in fconfigure -encoding\&.) +.TP +\fB-translation\fR \fItrans\fR +Read files with this translation\&. (As in fconfigure -translation\&.) +.RE +.TP +\fB::DiffUtil::compareStreams\fR ?\fIoptions\fR? \fIch1\fR \fIch2\fR +Compare two channel streams\&. +The return value is a boolean which is true when equal\&. +.RS +.TP +\fB-nocase\fR +Ignore case\&. +.TP +\fB-ignorekey\fR +Ignore keyword substitutions\&. This is limited to the first 60k read\&. +.TP +\fB-binary\fR +Treat stream as binary data\&. Normally this means it is configured +with -translation binary\&. +.RE .PP .SH EXAMPLES .PP .CS Index: undroid/DiffUtilTcl/generic/comparefiles.c ================================================================== --- undroid/DiffUtilTcl/generic/comparefiles.c +++ undroid/DiffUtilTcl/generic/comparefiles.c @@ -11,10 +11,312 @@ #include #include #include #include #include "diffutil.h" + +/* Visual C++ does not define S_ISDIR */ +#ifndef S_ISDIR +#define S_ISDIR(mode_) (((mode_) & _S_IFMT) == _S_IFDIR) +#endif + +const int BlockRead_C = 65536; + +typedef struct { + int ignoreKey; + int noCase; + int binary; +} CmpOptions_T; + +/* Helper to get a filled in CmpOptions_T */ +#define InitCmpOptions_T(opts) {opts.ignoreKey = 0; opts.noCase = 0; opts.binary = 0;} + +/* This is called when a dollar is encountered during ignore-keyword. + If return is equal res1/2 says how far we covered and considered equal. + This do not bother with encoding since the chars we are interested in + are the same in ascii/binary/utf8. */ +static int +ScanKey( + const char *s1, + const char *s2, + const char *e1, + const char *e2, + const char **res1, + const char **res2) +{ + *res1 = s1; + *res2 = s2; + /* Scan word chars until : or $ ends the keyword. + They must be equal up to that point. */ + while (s1 < e1 && s2 < e2) { + if ((*s1 == ':' || *s1 == '$') && (*s2 == ':' || *s2 == '$')) { + /* The keyword part has ended on both sides */ + + /* To be a bit conservative and not confuse keywords with e.g. + Tcl namespace variables we only acknowledge these forms: + keyword$ + keyword:$ + keyword: .*$ + keyword:: .*$ + */ + if (*s1 == ':') { + s1++; + if (s1 + 1 >= e1) { + return 1; + } + /* May be a double colon */ + if (*s1 == ':') { + s1++; + } + /* Colon must be followed by space or $ */ + if (*s1 != ' ' && *s1 != '$') { + return 1; + } + } + if (*s2 == ':') { + s2++; + if (s2 + 1 >= e2) { + return 1; + } + if (*s2 == ':') { + s2++; + } + if (*s2 != ' ' && *s2 != '$') { + return 1; + } + } + break; + } + if (*s1 != *s2) { + /* They are not equal keywords */ + return 0; + } + /* Only standard ascii word chars count */ + if ((*s1 >= 'a' && *s1 <= 'z') || (*s1 >= 'A' && *s1 <= 'Z')) { + s1++; + s2++; + } else { + /* This did not count as a keyword but is sofar equal. */ + *res1 = s1; + *res2 = s2; + return 1; + } + } + /* Skip all until $ */ + while (s1 < e1) { + if (*s1 == '$') { + break; + } + s1++; + } + while (s2 < e2) { + if (*s2 == '$') { + break; + } + s2++; + } + /* At this point s1/2 should point to the dollar ending the keyword. */ + if (s1 == e1 || s2 == e2) { + /* We reached the end of string without finishing the keyword. + If a potential keyword is at the end we don't care. */ + return 1; + } + /* Strings are equal up to this point. Skip the last dollar as well. */ + *res1 = s1 + 1; + *res2 = s2 + 1; + return 1; +} + +/* Compare two strings, ignoring keywords. + If return is true, they are equal up to the point of res1/2. */ +static int +CompareNoKey( + const char *s1, /* UTF string to compare to s2. */ + const char *s2, /* UTF string s2 is compared to. */ + unsigned long len1, /* Number of bytes to compare. */ + unsigned long len2, + CmpOptions_T *cmpOptions, + const char **res1, + const char **res2) +{ + Tcl_UniChar ch1 = 0, ch2 = 0; + const char *end1 = s1 + len1; + const char *end2 = s2 + len2; + const char *scan1, *scan2; + + while (s1 < end1 && s2 < end2) { + if (cmpOptions->binary) { + ch1 = *(s1++); + ch2 = *(s2++); + } else { + s1 += Tcl_UtfToUniChar(s1, &ch1); + s2 += Tcl_UtfToUniChar(s2, &ch2); + } + if (ch1 != ch2) { + if (cmpOptions->noCase) { + ch1 = Tcl_UniCharToLower(ch1); + ch2 = Tcl_UniCharToLower(ch2); + if (ch1 != ch2) { + return 0; + } + } else { + return 0; + } + } + if (ch1 == '$') { + if (!ScanKey(s1, s2, end1, end2, &scan1, &scan2)) { + /* If ScanKey said not equal, we trust it unless we use + less strict rules */ + if (!cmpOptions->noCase) { + return 0; + } + } + s1 = scan1; + s2 = scan2; + } + } + *res1 = s1; + *res2 = s2; + return 1; +} + +static int +CompareStreams( + Tcl_Channel ch1, + Tcl_Channel ch2, + CmpOptions_T *cmpOptions) +{ + int equal; + Tcl_Obj *line1Ptr, *line2Ptr; + int charactersRead1, charactersRead2; + int length1, length2; + int firstblock; + const char *string1, *string2; + + /* Initialize an object to use as line buffer. */ + line1Ptr = Tcl_NewObj(); + line2Ptr = Tcl_NewObj(); + Tcl_IncrRefCount(line1Ptr); + Tcl_IncrRefCount(line2Ptr); + if (cmpOptions->binary) { + Tcl_SetByteArrayLength(line1Ptr, 70000); + Tcl_SetByteArrayLength(line2Ptr, 70000); + } else { + Tcl_SetObjLength(line1Ptr, 70000); + Tcl_SetObjLength(line2Ptr, 70000); + } + + equal = 1; + firstblock = 1; + while (equal) { + charactersRead1 = Tcl_ReadChars(ch1, line1Ptr, BlockRead_C, 0); + charactersRead2 = Tcl_ReadChars(ch2, line2Ptr, BlockRead_C, 0); + if (charactersRead1 <= 0 && charactersRead2 <= 0) { + /* Nothing more to check */ + break; + } + if (charactersRead1 <= 0 || charactersRead2 <= 0) { + /* One of the files is ended */ + equal = 0; + break; + } + if (charactersRead1 != charactersRead2 && !cmpOptions->ignoreKey) { + /* Different size */ + equal = 0; + break; + } + if (cmpOptions->binary) { + string1 = (char *) Tcl_GetByteArrayFromObj(line1Ptr, &length1); + string2 = (char *) Tcl_GetByteArrayFromObj(line2Ptr, &length2); + } else { + string1 = Tcl_GetStringFromObj(line1Ptr, &length1); + string2 = Tcl_GetStringFromObj(line2Ptr, &length2); + } + /* Limit ignoreKey to first block to simplify. + No need to handle keywords crossing block boundary and a bit + better performance on large files. */ + if (firstblock && cmpOptions->ignoreKey) { + const char *res1, *res2; + unsigned long rem1, rem2; + int eq = CompareNoKey(string1, string2, length1, length2, + cmpOptions, &res1, &res2); + if (!eq) { + equal = 0; + break; + } + firstblock = 0; + /* Did we consume everything? */ + if (res1 >= string1 + length1 && res2 >= string2 + length2) { + continue; + } + /* Compensate for any change in length. */ + rem1 = length1 - (res1 - string1); + rem2 = length2 - (res2 - string2); + /* Only one side should have anything left. */ + if (rem1 > 0 && rem2 > 0) { Tcl_Panic("PANIC"); } + if (rem1 > 0) { + unsigned long chars1; + /* Adjust side 1 */ + string1 = res1; + length1 = rem1; + chars1 = Tcl_NumUtfChars(string1, length1); + /* Read extra characters from side 2 */ + charactersRead2 = Tcl_ReadChars(ch2, line2Ptr, chars1, 0); + if (charactersRead2 <= 0) { + equal = 0; + break; + } + if (cmpOptions->binary) { + string2 = (char *) Tcl_GetByteArrayFromObj(line2Ptr, &length2); + } else { + string2 = Tcl_GetStringFromObj(line2Ptr, &length2); + } + } else { /* rem2 > 0 */ + unsigned long chars2; + /* Adjust side 2 */ + string2 = res2; + length2 = rem2; + chars2 = Tcl_NumUtfChars(string2, length2); + /* Read extra characters from side 1 */ + charactersRead1 = Tcl_ReadChars(ch1, line1Ptr, chars2, 0); + if (charactersRead1 <= 0) { + equal = 0; + break; + } + if (cmpOptions->binary) { + string1 = (char *) Tcl_GetByteArrayFromObj(line1Ptr, &length1); + } else { + string1 = Tcl_GetStringFromObj(line1Ptr, &length1); + } + } + } + if (length1 != length2) { + equal = 0; + break; + } + if (cmpOptions->binary) { + if (strncmp(string1, string2, length1) != 0) { + equal = 0; + break; + } + } else if (cmpOptions->noCase) { + if (Tcl_UtfNcasecmp(string1, string2, length1) != 0) { + equal = 0; + break; + } + } else { + if (Tcl_UtfNcmp(string1, string2, length1) != 0) { + equal = 0; + break; + } + } + } + + Tcl_DecrRefCount(line1Ptr); + Tcl_DecrRefCount(line2Ptr); + return equal; +} int CompareFilesObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ @@ -21,21 +323,18 @@ int objc, /* Number of arguments. */ Tcl_Obj *CONST objv[]) /* Argument objects. */ { int index, t, result = TCL_OK; Tcl_Obj *file1Ptr, *file2Ptr; - int ignoreKey = 0, binary = 0, equal = 0; + int equal = 0; + CmpOptions_T cmpOptions; Tcl_Obj *encodingPtr = NULL; Tcl_Obj *translationPtr = NULL; Tcl_StatBuf *statBuf; Tcl_Channel ch1 = NULL, ch2 = NULL; - Tcl_Obj *line1Ptr, *line2Ptr; Tcl_WideUInt size1, size2; unsigned mode1, mode2; - int charactersRead1, charactersRead2; - int length1, length2; - char *string1, *string2; static CONST char *options[] = { "-nocase", "-ignorekey", "-encoding", "-translation", (char *) NULL }; @@ -46,22 +345,24 @@ if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "?opts? file1 file2"); return TCL_ERROR; } + InitCmpOptions_T(cmpOptions); for (t = 1; t < objc - 2; t++) { if (Tcl_GetIndexFromObj(interp, objv[t], options, "option", 0, &index) != TCL_OK) { result = TCL_ERROR; goto cleanup; } switch (index) { case OPT_NOCASE: + cmpOptions.noCase = 1; break; case OPT_IGNOREKEY: - ignoreKey = 1; + cmpOptions.ignoreKey = 1; break; case OPT_ENCODING: t++; if (t >= objc - 2) { Tcl_WrongNumArgs(interp, 1, objv, "?opts? file1 file2"); @@ -87,11 +388,11 @@ file2Ptr = objv[objc-1]; if (translationPtr != NULL) { char *valueName = Tcl_GetString(translationPtr); if (strcmp(valueName, "binary") == 0) { - binary = 1; + cmpOptions.binary = 1; } } statBuf = Tcl_AllocStatBuf(); @@ -119,15 +420,15 @@ if (S_ISDIR(mode1) || S_ISDIR(mode2)) { equal = 0; goto done; } /* On binary comparison, different size means different */ - if (binary && !ignoreKey && size1 != size2) { + if (cmpOptions.binary && !cmpOptions.ignoreKey && size1 != size2) { equal = 0; goto done; } - + ch1 = Tcl_FSOpenFileChannel(interp, file1Ptr, "r", 0); if (ch1 == NULL) { result = TCL_ERROR; goto cleanup; } @@ -160,72 +461,11 @@ != TCL_OK) { result = TCL_ERROR; goto cleanup; } } - - /* Initialize an object to use as line buffer. */ - line1Ptr = Tcl_NewObj(); - line2Ptr = Tcl_NewObj(); - Tcl_IncrRefCount(line1Ptr); - Tcl_IncrRefCount(line2Ptr); - if (binary) { - Tcl_SetByteArrayLength(line1Ptr, 70000); - Tcl_SetByteArrayLength(line2Ptr, 70000); - } else { - Tcl_SetObjLength(line1Ptr, 70000); - Tcl_SetObjLength(line2Ptr, 70000); - } - - equal = 1; - while (1) { - charactersRead1 = Tcl_ReadChars(ch1, line1Ptr, 65536, 0); - charactersRead2 = Tcl_ReadChars(ch2, line2Ptr, 65536, 0); - if (charactersRead1 <= 0 && charactersRead2 <= 0) { - /* Nothing more to check */ - break; - } - if (charactersRead1 <= 0 || charactersRead2 <= 0) { - /* One of the files is ended */ - equal = 0; - break; - } - if (charactersRead1 != charactersRead2 && !ignoreKey) { - /* Different size */ - equal = 0; - break; - } - if (binary) { - string1 = (char *) Tcl_GetByteArrayFromObj(line1Ptr, &length1); - string2 = (char *) Tcl_GetByteArrayFromObj(line2Ptr, &length2); - } else { - string1 = Tcl_GetStringFromObj(line1Ptr, &length1); - string2 = Tcl_GetStringFromObj(line2Ptr, &length2); - } - if (ignoreKey) { - - } else { - if (length1 != length2) { - equal = 0; - break; - } - if (binary) { - if (strncmp(string1, string2, length1) != 0) { - equal = 0; - break; - } - } else { - if (Tcl_UtfNcmp(string1, string2, length1) != 0) { - equal = 0; - break; - } - } - } - } - - Tcl_DecrRefCount(line1Ptr); - Tcl_DecrRefCount(line2Ptr); + equal = CompareStreams(ch1, ch2, &cmpOptions); done: Tcl_SetObjResult(interp, Tcl_NewIntObj(equal)); cleanup: @@ -243,5 +483,68 @@ Tcl_DecrRefCount(translationPtr); } return result; } + +int +CompareStreamsObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument objects. */ +{ + int index, t, result = TCL_OK; + int equal; + CmpOptions_T cmpOptions; + Tcl_Channel ch1, ch2; + + static CONST char *options[] = { + "-nocase", "-ignorekey", "-binary", + (char *) NULL + }; + enum options { + OPT_NOCASE, OPT_IGNOREKEY, OPT_BINARY + }; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "?opts? ch1 ch2"); + return TCL_ERROR; + } + InitCmpOptions_T(cmpOptions); + + for (t = 1; t < objc - 2; t++) { + if (Tcl_GetIndexFromObj(interp, objv[t], options, "option", 0, + &index) != TCL_OK) { + result = TCL_ERROR; + goto cleanup; + } + switch (index) { + case OPT_NOCASE: + cmpOptions.noCase = 1; + break; + case OPT_IGNOREKEY: + cmpOptions.ignoreKey = 1; + break; + case OPT_BINARY: + cmpOptions.binary = 1; + break; + } + } + + ch1 = Tcl_GetChannel(interp, Tcl_GetString(objv[objc-2]), NULL); + if (ch1 == NULL) { + result = TCL_ERROR; + goto cleanup; + } + ch2 = Tcl_GetChannel(interp, Tcl_GetString(objv[objc-1]), NULL); + if (ch2 == NULL) { + result = TCL_ERROR; + goto cleanup; + } + + equal = CompareStreams(ch1, ch2, &cmpOptions); + Tcl_SetObjResult(interp, Tcl_NewIntObj(equal)); + + cleanup: + return result; +} Index: undroid/DiffUtilTcl/generic/diff.c ================================================================== --- undroid/DiffUtilTcl/generic/diff.c +++ undroid/DiffUtilTcl/generic/diff.c @@ -24,10 +24,17 @@ /* A type for the parsing state */ typedef enum { IN_NONE, IN_SPACE, IN_NUMBER } In_T; +/* + * max already defined by Visual C++, but reuse our def just in case + * semantics differ. + */ +#ifdef max +#undef max /* Unset to prevent preprocessor warnings */ +#endif #define max(a,b) ((a) > (b) ? (a) : (b)) /* A type to implement the Candidates in the LCS algorithm */ typedef struct Candidate_T { /* Line numbers in files */ @@ -939,14 +946,15 @@ } /* * In principle this matching should be a rerun of LCS on the block, with * forbidden lines allowed. - * If the group is larger than the pivot, we do not want to recurse to LCS + * If the group is much larger than the pivot, we do not want to recurse to LCS * since that would just repeat the timing problem we try to avoid. + * Small lists are ok (we should not skip this when pivot=1). */ - if (jList->n < optsPtr->pivot) { + if (jList->n < 20 || jList->n < optsPtr->pivot * 2) { /* TBD how much larger is ok? */ int anyForbidden; Line_T *newJ; DiffOptions_T opts = *optsPtr; opts.rFrom1 = firstI; opts.rTo1 = lastI; @@ -964,20 +972,20 @@ /* * Just do a raw sequential matching of forbidden lines. * This produces a reasonable, if non-optimal, result. * FIXA: Better algorithm here? - */ + */ for (j = 0; j < iList->n && j < jList->n; j++) { Line_T line1 = iList->Elems[j].line; Line_T line2 = jList->Elems[j].line; if (IsLineMatch(&iList->Elems[j], &jList->Elems[j], optsPtr)) { J[line1] = line2; } } } - + /* * We have ignored forbidden lines before which means that there * may be more lines that can be matched. */ static void @@ -1046,11 +1054,11 @@ /* Make a list of all forbidden lines in this change block. */ AddToLineList(&iList, i, P[i].hash); continue; } } - + FreeLineList(&iList); FreeLineList(&jList); /* * This could be left to block matching if that is improved * to handle equal lines within change blocks. @@ -1078,12 +1086,12 @@ /* * The core part of the LCS algorithm. * It is independent of data since it only works on hashes. * * This is the inner part of it, which do not meddle with forbidden lines. - * It respects them, but do not add or clean up any forbidden lines. - * + * It normally respects them, but do not add or clean up any forbidden lines. + * * Returns the J vector as a ckalloc:ed array. */ static Line_T * LcsCoreInner( Tcl_Interp *interp, @@ -1091,17 +1099,17 @@ Line_T n, /* number of elements in second sequence */ const P_T *P, /* The P vector [0,m] corresponds to lines in "file 1" */ const E_T *E, /* The E vector [0,n] corresponds to lines in "file 2" */ const DiffOptions_T *optsPtr, int ignoreForbidden, - int *anyForbidden) /* Out parameter, was any forbidden lines skipped? */ + int *anyForbidden) /* Out parameter: Was any forbidden lines skipped? */ { Candidate_T **K, *c; Line_T i, k, *J; /* Keep track of all candidates to free them easily */ CandidateAlloc_T *candidates = NULL; - + *anyForbidden = 0; /*printf("Doing K\n"); */ /* Initialise K candidate vector */ @@ -1273,11 +1281,11 @@ P_T *P, E_T *E, const DiffOptions_T *optsPtr) { Line_T i, *J; int anyForbidden; - + for (i = 1; i <= m; i++) { if (P[i].Eindex != 0) { if (optsPtr->noempty && P[i].hash == 0) { /* Uphold the -noempty rule by forbidding those connections */ ForbidP(i, P, E); @@ -1694,12 +1702,12 @@ DiffOptsRegsub( Tcl_Interp *interp, /* Current interpreter. */ Tcl_Obj *obj1Ptr, /* Input object. */ Tcl_Obj *rePtr, /* Regexp object. */ Tcl_Obj *sub1Ptr, /* Substitution. */ - Tcl_Obj **resultPtrPtr, /* Store result, if successful. */ - const DiffOptions_T *optsPtr) /* Options. */ + Tcl_Obj **resultPtrPtr, /* Store result, if successful. */ + const DiffOptions_T *optsPtr) /* Options. */ { int idx, result, cflags, all, wlen, wsublen, numMatches, offset; int start, end, subStart, subEnd, match; Tcl_RegExp regExpr; Tcl_RegExpInfo info; Index: undroid/DiffUtilTcl/generic/diffstrings.c ================================================================== --- undroid/DiffUtilTcl/generic/diffstrings.c +++ undroid/DiffUtilTcl/generic/diffstrings.c @@ -118,11 +118,11 @@ int igSpace = (optsPtr->ignore & (IGNORE_SPACE_CHANGE | IGNORE_ALL_SPACE)); int word = optsPtr->wordparse; resPtr = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(resPtr); - + string = Tcl_GetStringFromObj(strPtr, &length); str = string; startWord = str; while (*str != 0) { endWord = str; @@ -407,11 +407,11 @@ Tcl_ListObjAppendElement(interp, *resPtr, emptyPtr); Tcl_ListObjAppendElement(interp, *resPtr, emptyPtr); } return; } - + if ((optsPtr->ignore & (IGNORE_SPACE_CHANGE | IGNORE_ALL_SPACE)) || optsPtr->wordparse) { /* Do it chunk-wise through list diff */ CompareStringsL(interp, str1Ptr, str2Ptr, optsPtr, resPtr); return; Index: undroid/DiffUtilTcl/generic/diffutil.c ================================================================== --- undroid/DiffUtilTcl/generic/diffutil.c +++ undroid/DiffUtilTcl/generic/diffutil.c @@ -30,11 +30,11 @@ Tcl_UniChar first1, c2; /* * We are searching string2 for the sequence string1. */ - + match = -1; if (length1 < 0) length1 = Tcl_UniCharLen(ustring1); if (length2 < 0) length2 = Tcl_UniCharLen(ustring2); @@ -202,11 +202,11 @@ /* Handle middle (common) part*/ Tcl_ListObjAppendElement(interp, res, Tcl_NewUnicodeObj(str1 + found1, foundlen)); Tcl_ListObjAppendElement(interp, res, Tcl_NewUnicodeObj(str2 + found2, foundlen)); - + /* Handle right part, recursively*/ apa1 = Tcl_NewUnicodeObj(str1 + found1 + foundlen, -1); apa2 = Tcl_NewUnicodeObj(str2 + found2 + foundlen, -1); Tcl_IncrRefCount(apa1); Tcl_IncrRefCount(apa2); @@ -234,17 +234,17 @@ static CONST char *options[] = { "-nocase", "-i", "-b", "-w", "-words", (char *) NULL }; enum options { OPT_NOCASE, OPT_I, OPT_B, OPT_W, OPT_WORDS - }; + }; if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "?opts? line1 line2"); return TCL_ERROR; } - + for (t = 1; t < objc - 2; t++) { if (Tcl_GetIndexFromObj(interp, objv[t], options, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } @@ -265,11 +265,11 @@ } } line1 = Tcl_GetUnicodeFromObj(objv[objc-2], &len1); line2 = Tcl_GetUnicodeFromObj(objv[objc-1], &len2); - + s1 = line1; s2 = line2; e1 = line1 + len1; e2 = line2 + len2; @@ -278,11 +278,11 @@ while (s1 < e1 && Tcl_UniCharIsSpace(*s1)) s1++; while (s2 < e2 && Tcl_UniCharIsSpace(*s2)) s2++; while (e1 > s1 && Tcl_UniCharIsSpace(*(e1-1))) e1--; while (e2 > s2 && Tcl_UniCharIsSpace(*(e2-1))) e2--; } - + /* Skip matching chars in both ends * Forwards */ word1 = s1; word2 = s2; wordflag = 0; while (s1 < e1 && s2 < e2) { @@ -384,10 +384,11 @@ if (Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION) != TCL_OK) { return TCL_ERROR; } TCOC("DiffUtil::compareFiles", CompareFilesObjCmd); + TCOC("DiffUtil::compareStreams", CompareStreamsObjCmd); TCOC("DiffUtil::diffFiles", DiffFilesObjCmd); TCOC("DiffUtil::diffLists", DiffListsObjCmd); TCOC("DiffUtil::diffStrings", DiffStringsObjCmd); TCOC("DiffUtil::diffStrings2", DiffStrings2ObjCmd); Tcl_SetVar(interp, "DiffUtil::version", PACKAGE_VERSION, TCL_GLOBAL_ONLY); Index: undroid/DiffUtilTcl/generic/diffutil.h ================================================================== --- undroid/DiffUtilTcl/generic/diffutil.h +++ undroid/DiffUtilTcl/generic/diffutil.h @@ -45,11 +45,11 @@ Line_T staticAlign[STATIC_ALIGN]; } DiffOptions_T; /* Helper to get a filled in DiffOptions_T */ #define InitDiffOptions_T(opts) {opts.ignore = 0; opts.noempty = 0; opts.pivot = 10; opts.wordparse = 0; opts.rFrom1 = 1; opts.rTo1 = 0; opts.rFrom2 = 1; opts.rTo2 = 0; opts.regsubLeftPtr = NULL; opts.regsubRightPtr = NULL; opts.resultStyle = Result_Diff; opts.firstIndex = 1; opts.alignLength = 0; opts.align = opts.staticAlign;} - + /* Flags in DiffOptions_T's ignore field */ #define IGNORE_ALL_SPACE 1 #define IGNORE_SPACE_CHANGE 2 #define IGNORE_CASE 4 @@ -142,10 +142,15 @@ extern int CompareFilesObjCmd(ClientData dummy, Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]); +extern int +CompareStreamsObjCmd(ClientData dummy, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); extern int DiffFilesObjCmd(ClientData dummy, Index: undroid/DiffUtilTcl/makevfs.sh ================================================================== --- undroid/DiffUtilTcl/makevfs.sh +++ undroid/DiffUtilTcl/makevfs.sh @@ -1,6 +1,12 @@ # Script to build a multi-platform vfs + +# Do this separately on mac: +# ./configure +# make clean +# make +# make vfsmac # Cross-compiling needs a cross-compiled tcl for linking ./configure --host=i586-mingw32msvc --target=i586-mingw32msvc --with-tcl=/home/peter/tcl/win/tcl/win make clean make @@ -14,5 +20,9 @@ ./configure --enable-64bit --with-tcl=/home/peter/tcl/v86/tcl/unix make clean make make vfs64 + +# End with a sanity check +ls -l lib.vfs/DiffUtil/*/*.so lib.vfs/DiffUtil/*/*.dylib lib.vfs/DiffUtil/*/*.dll +file lib.vfs/DiffUtil/*/*.so lib.vfs/DiffUtil/*/*.dylib lib.vfs/DiffUtil/*/*.dll Index: undroid/DiffUtilTcl/tcl/diffutil.tcl ================================================================== --- undroid/DiffUtilTcl/tcl/diffutil.tcl +++ undroid/DiffUtilTcl/tcl/diffutil.tcl @@ -9,11 +9,11 @@ # #---------------------------------------------------------------------- # $Revision: 1.12 $ #---------------------------------------------------------------------- -package provide DiffUtil 0.4.0 +package provide DiffUtil 0.4.1 namespace eval DiffUtil { namespace export diffFiles diffStrings variable version [package provide DiffUtil] variable implementation "tcl" Index: undroid/DiffUtilTcl/tests/comparefiles.test ================================================================== --- undroid/DiffUtilTcl/tests/comparefiles.test +++ undroid/DiffUtilTcl/tests/comparefiles.test @@ -26,11 +26,11 @@ close $ch set ch [open _diff_2 wb] puts -nonewline $ch $data2 close $ch - set apa [catch {eval DiffUtil::compareFiles $args _diff_1 _diff_2} res] + set apa [catch {DiffUtil::compareFiles {*}$args _diff_1 _diff_2} res] file delete -force _diff_1 _diff_2 if {$apa} { return [list $apa $res] } return $res @@ -63,10 +63,16 @@ test comparefiles-1.5 {standard cases} { set l1 {abcd} set l2 {abce} RunTest $l1 $l2 } 0 + +test comparefiles-1.6 {standard cases} { + set l1 {abcd} + set l2 {abcD} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -nocase] +} {0 1} test comparefiles-2.1 {channel config} {CDiff} { set l1 {a b c} set l2 {a d c} RunTest $l1 $l2 -encoding iso8859-1 -translation lf ADDED undroid/DiffUtilTcl/tests/comparestreams.test Index: undroid/DiffUtilTcl/tests/comparestreams.test ================================================================== --- /dev/null +++ undroid/DiffUtilTcl/tests/comparestreams.test @@ -0,0 +1,139 @@ +# Tests for the 'DiffUtil' package. -*- tcl -*- +# +# Copyright (c) 2010 by Peter Spjuth. All rights reserved. + +package require DiffUtil + +if {[lsearch [namespace children] ::tcltest] == -1} { + package require tcltest + namespace import ::tcltest::* +} + +# There are differences between the C and the Diff result. +tcltest::testConstraint CDiff \ + [expr {[info proc DiffUtil::ExecDiffFiles] == ""}] +tcltest::testConstraint TclDiff \ + [expr {[info proc DiffUtil::ExecDiffFiles] != ""}] + +# All tests are for C only +if {[info proc DiffUtil::ExecDiffFiles] != ""} return +#---------------------------------------------------------------------- + +# A wrapper to simplify calling +proc RunTest {data1 data2 args} { + set ch [open _diff_1 wb] + puts -nonewline $ch $data1 + close $ch + set ch [open _diff_2 wb] + puts -nonewline $ch $data2 + close $ch + + set ch1 [open _diff_1 rb] + set ch2 [open _diff_2 rb] + + set apa [catch {DiffUtil::compareStreams {*}$args $ch1 $ch2} res] + close $ch1 + close $ch2 + file delete -force _diff_1 _diff_2 + if {$apa} { + return [list $apa $res] + } + return $res +} + +#---------------------------------------------------------------------- + +test comparestreams-1.1 {standard cases} { + set l1 {abcd} + set l2 {abcd} + RunTest $l1 $l2 +} 1 + +test comparestreams-1.2 {standard cases, error} -body { + set l1 {a b c d f g h i j k l} + set l2 { b c d e f g x y k l} + RunTest $l1 $l2 -hubba +} -result [list 1 {bad option "-hubba"*}] -match glob + +test comparestreams-1.3 {standard cases, error} -body { + DiffUtil::compareStreams a +} -returnCodes 1 -result "wrong # args*" -match glob + +test comparestreams-1.4 {standard cases} { + set l1 {abcd} + set l2 {abcde} + RunTest $l1 $l2 +} 0 + +test comparestreams-1.5 {standard cases} { + set l1 {abcd} + set l2 {abce} + RunTest $l1 $l2 +} 0 + +test comparestreams-1.6 {standard cases} { + set l1 {abcd} + set l2 {abcD} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -nocase] +} {0 1} + +test comparestreams-2.1 {keywords} { + set l1 {abcd} + set l2 {abcD} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 0} + +test comparestreams-2.2 {keywords} { + set l1 {abcd $apa$} + set l2 {abcd $apa: hej$} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 1} + +test comparestreams-2.3 {keywords} { + set l1 {abcd $apa::$} + set l2 {abcd $apa: hej$} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 1} + +test comparestreams-2.4 {keywords vs namespace vars} { + set l1 {abcd $apa::hej$apa::hopp} + set l2 {abcd $apa::haj$apa::hipp} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 0} + +test comparestreams-2.5 {keywords} { + set l1 {abcd $apa: hejsan$ hopp} + set l2 {abcd $apa: hoppsan$ hopp} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 1} + +test comparestreams-2.6 {keywords} { + # Longer version of 2.5. Handle larger than block size. + set l1 {abcd $apa: hejsan$ hopp} + set l2 {abcd $apa: hoppsan$ hopp} + append l1 [string repeat gurka 20000] + append l2 [string repeat gurka 20000] + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 1} + +test comparestreams-2.7 {keywords} { + set l1 {abcd $apa: hejsan$ hopp $bepa: hugo$ nej} + set l2 {abcd $apa: hoppsan$ hopp $bepa: apa$ nej} + list [RunTest $l1 $l2] [RunTest $l1 $l2 -ignorekey] +} {0 1} + +test comparestreams-2.8 {keywords} { + set base1 {abcd $apa: hejsan$ hopp} + set base2 {abcd $apa: hoppsan$ hopp} + set lastOk 0 + # This will eventually fail when the keyword reaches the block size. + for {set l 65500} {$l < 65540} {incr l} { + set s1 [string repeat x $l]$base1 + set s2 [string repeat x $l]$base2 + if {[RunTest $s1 $s2 -ignorekey]} { + set lastOk $l + } + } + # Check that it worked long enough, but not too long + expr {$lastOk > 65510 && $lastOk != 65539 ? 1 : $lastOk} +} {1} Index: undroid/DiffUtilTcl/tests/difffiles.test ================================================================== --- undroid/DiffUtilTcl/tests/difffiles.test +++ undroid/DiffUtilTcl/tests/difffiles.test @@ -417,11 +417,11 @@ } [list [list {2 1 2 1}] {}] test difffiles-12.1 {stack space bug} -body { # These files consume lots of candidate stack and crashed an earlier # version of diffUtil. - DiffUtil::diffFiles -b tests/candbug1.txt tests/candbug2.txt + DiffUtil::diffFiles -b [file join [configure -testdir] candbug1.txt] [file join [configure -testdir] candbug2.txt] return 0 } -result {0} test difffiles-13.1 {long runtime bug} -constraints {CDiff} -body { # This case took very long time in an earlier version @@ -449,11 +449,11 @@ set result [RunTest $l1 $l2 -pivot 900] } -result {} test difffiles-14.1 {pivot bug} -constraints CDiff -body { # These files caused an error in post-processing of forbidden lines - set x [DiffUtil::diffFiles -pivot 100 tests/pivot1.txt tests/pivot2.txt] + set x [DiffUtil::diffFiles -pivot 100 [file join [configure -testdir] pivot1.txt] [file join [configure -testdir] pivot2.txt]] llength $x } -result {160} test difffiles-15.1a {forbidden stressing} -constraints {NoLcsDiff} -body { # Try to stress the postprocessing of forbidden matches Index: undroid/DiffUtilTcl/win/makefile.vc ================================================================== --- undroid/DiffUtilTcl/win/makefile.vc +++ undroid/DiffUtilTcl/win/makefile.vc @@ -1,467 +1,40 @@ -# 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. -# +#------------------------------------------------------------- -*- makefile -*- +# +# Makefile for diffutil +# +# Basic build, test and install +# nmake /f makefile.vc INSTALLDIR=c:\tcl +# nmake /f makefile.vc INSTALLDIR=c:\tcl test +# nmake /f makefile.vc INSTALLDIR=c:\tcl install +# +# For other build options (debug, static etc.), +# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for +# detailed documentation. +# # 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.14 2010/09/14 23:23:32 hobbs 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 sample05.dll and -# would install it into $(INSTALLDIR)\lib\sample05 -# -# You need to specify the object files that need to be linked into your -# binary here. -# -#------------------------------------------------------------------------- - -PROJECT = diffutil - -# Uncomment the following line if this is a Tk extension. -#PROJECT_REQUIRES_TK=1 -!include "rules.vc" - -DOTVERSION = 0.4.0 -VERSION = $(DOTVERSION:.=) -STUBPREFIX = $(PROJECT)stub - -DLLOBJS = \ +# +#------------------------------------------------------------------------------ + + +PROJECT = DiffUtil + +!include "rules-ext.vc" + +PRJ_OBJS = \ $(TMP_DIR)\diffutil.obj \ $(TMP_DIR)\diff.obj \ -!if !$(STATIC_BUILD) - $(TMP_DIR)\diffutil.res -!endif - -#------------------------------------------------------------------------- -# 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)\win -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 = $(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) -DBUILD_$(PROJECT) $(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 -!endif -!endif - -INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" -BASE_CFLAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) -CON_CFLAGS = $(cflags) $(cdebug) $(crt) -DCONSOLE -TCL_CFLAGS = -DPACKAGE_NAME="\"$(PROJECT)\"" \ - -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \ - -DBUILD_$(PROJECT) \ - $(BASE_CFLAGS) $(OPTDEFINES) - -#--------------------------------------------------------------------- -# Link flags -#--------------------------------------------------------------------- - -!if $(DEBUG) -ldebug = -debug:full -debugtype:cv -!if $(MSVCRT) -ldebug = $(ldebug) -nodefaultlib:msvcrt -!endif -!else -ldebug = -release -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" -!if $(VCVERSION) >= 1400 && $(VCVERSION) < 1500 -baselibs = $(baselibs) bufferoverflowU.lib -!endif -!endif - -baselibs = $(baselibs) user32.lib gdi32.lib - -#--------------------------------------------------------------------- -# TclTest flags -#--------------------------------------------------------------------- - -!IF "$(TESTPAT)" != "" -TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT) -!ENDIF - -#--------------------------------------------------------------------- -# Project specific targets (EDIT) -#--------------------------------------------------------------------- - -all: setup $(PROJECT) -$(PROJECT): setup pkgIndex $(PRJLIB) -install: install-binaries install-libraries install-docs -pkgIndex: $(OUT_DIR)\pkgIndex.tcl - -test: setup $(PROJECT) - @set TCL_LIBRARY=$(TCL_LIBRARY:\=/) - @set TCLLIBPATH=$(OUT_DIR_PATH:\=/) -!if $(TCLINSTALL) - @set PATH=$(_TCLDIR)\bin;$(PATH) -!else - @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH) -!endif -!if "$(OS)" == "Windows_NT" || "$(MSVCDIR)" == "IDE" - $(DEBUGGER) $(TCLSH) "$(ROOT)/tests/all.tcl" $(TESTFLAGS) -!else - @echo Please wait while the tests are collected... - $(DEBUGGER) $(TCLSH) "$(ROOT)/tests/all.tcl" $(TESTFLAGS) > tests.log - type tests.log | more -!endif - -shell: setup $(PROJECT) - @set VLERQ_LIBRARY=$(LIBDIR:\=/) - @set TCL_LIBRARY=$(TCL_LIBRARY:\=/) - @set TCLLIBPATH=$(OUT_DIR_PATH:\=/) -!if $(TCLINSTALL) - @set PATH=$(_TCLDIR)\bin;$(PATH) -!else - @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH) -!endif - $(DEBUGGER) $(TCLSH) $(SCRIPT) - -setup: - @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR) - @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR) - -# See /win/coffbase.txt for extension base addresses. -$(PRJLIB): $(DLLOBJS) -!if $(STATIC_BUILD) - $(lib32) -nologo -out:$@ @<< -$** -<< -!else - $(link32) $(dlllflags) -base:0x10000000 -out:$@ $(baselibs) @<< -$** -<< - $(_VC_MANIFEST_EMBED_DLL) - -@del $*.exp -!endif - -$(PRJSTUBLIB): $(PRJSTUBOBJS) - $(lib32) -nologo -out:$@ $(PRJSTUBOBJS) - -#--------------------------------------------------------------------- -# Implicit rules -#--------------------------------------------------------------------- - -{$(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 .rc - -#------------------------------------------------------------------------- -# Explicit dependency rules -# -#------------------------------------------------------------------------- - -$(OUT_DIR)\pkgIndex.tcl: $(ROOT)\pkgIndex.tcl.in - @nmakehlp -s << $** > $@ -@PACKAGE_VERSION@ $(DOTVERSION) -@PACKAGE_NAME@ $(PROJECT) -@PKG_LIB_FILE@ $(PRJLIBNAME) -<< - @echo package ifneeded DiffUtil $(DOTVERSION) \ - [list load [file join $$dir $(PRJLIBNAME)] Sample] >> $@ - -#--------------------------------------------------------------------- -# 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 - -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)\version.vc del $(WINDIR)\version.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 + $(TMP_DIR)\comparefiles.obj \ + $(TMP_DIR)\difffiles.obj \ + $(TMP_DIR)\difflists.obj \ + $(TMP_DIR)\diffstrings.obj + +# Hide numerous warnings of size_t to int conversions (4244) and +# signed/unsigned mismatch (4018) as these may cause genuine warnings +# to be missed +PRJ_DEFINES = -wd4244 -wd4018 + +!include "$(_RULESDIR)\targets.vc" + +install: default-install-docs-html +pkgindex: default-pkgindex-tea Index: undroid/DiffUtilTcl/win/nmakehlp.c ================================================================== --- undroid/DiffUtilTcl/win/nmakehlp.c +++ undroid/DiffUtilTcl/win/nmakehlp.c @@ -7,13 +7,10 @@ * 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.7 2008/06/18 11:01:42 patthoyts Exp $ * ---------------------------------------------------------------------------- */ #define _CRT_SECURE_NO_DEPRECATE #include @@ -40,21 +37,20 @@ #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); -int QualifyPath(const char *path); -const char * GetVersionFromFile(const char *filename, const char *match); -DWORD WINAPI ReadFromPipe(LPVOID args); +static int CheckForCompilerFeature(const char *option); +static int CheckForLinkerFeature(const char **options, int count); +static int IsIn(const char *string, const char *substring); +static int SubstituteFile(const char *substs, const char *filename); +static int QualifyPath(const char *path); +static int LocateDependency(const char *keyfile); +static const char *GetVersionFromFile(const char *filename, const char *match, int numdots); +static DWORD WINAPI ReadFromPipe(LPVOID args); /* globals */ #define CHUNK 25 #define STATICBUFFERSIZE 1000 @@ -76,10 +72,11 @@ char *argv[]) { char msg[300]; DWORD dwWritten; int chars; + char *s; /* * Make sure children (cl.exe and link.exe) are kept quiet. */ @@ -104,20 +101,20 @@ &dwWritten, NULL); return 2; } return CheckForCompilerFeature(argv[2]); case 'l': - if (argc != 3) { + if (argc < 3) { chars = snprintf(msg, sizeof(msg) - 1, - "usage: %s -l \n" + "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]); + return CheckForLinkerFeature(&argv[2], argc-2); case 'f': if (argc == 2) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -f \n" "Find a substring within another\n" @@ -132,22 +129,10 @@ 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" @@ -167,35 +152,52 @@ argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 0; } - printf("%s\n", GetVersionFromFile(argv[2], argv[3])); - return 0; + s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0'); + if (s && *s) { + printf("%s\n", s); + return 0; + } else + return 1; /* Version not found. Return non-0 exit code */ + case 'Q': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, - "usage: %s -q path\n" + "usage: %s -Q path\n" "Emit the fully qualified path\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return QualifyPath(argv[2]); + + case 'L': + if (argc != 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -L keypath\n" + "Emit the fully qualified path of directory containing keypath\n" + "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return LocateDependency(argv[2]); } } chars = snprintf(msg, sizeof(msg) - 1, - "usage: %s -c|-l|-f|-g|-V|-s|-Q ...\n" + "usage: %s -c|-f|-l|-Q|-s|-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 +static int CheckForCompilerFeature( const char *option) { STARTUPINFO si; PROCESS_INFORMATION pi; @@ -276,11 +278,11 @@ "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); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); return 2; } /* * Close our references to the write handles that have now been inherited. @@ -325,22 +327,24 @@ || strstr(Err.buffer, "D9002") != NULL || strstr(Out.buffer, "D2021") != NULL || strstr(Err.buffer, "D2021") != NULL); } -int +static int CheckForLinkerFeature( - const char *option) + const char **options, + int count) { STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; DWORD threadID; char msg[300]; BOOL ok; HANDLE hProcess, h, pipeThreads[2]; - char cmdline[100]; + int i; + char cmdline[255]; hProcess = GetCurrentProcess(); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); @@ -382,11 +386,15 @@ /* * Append our option for testing. */ - lstrcat(cmdline, option); + for (i = 0; i < count; i++) { + lstrcat(cmdline, " \""); + lstrcat(cmdline, options[i]); + lstrcat(cmdline, "\""); + } ok = CreateProcess( NULL, /* Module name. */ cmdline, /* Command line. */ NULL, /* Process handle not inheritable. */ @@ -404,11 +412,11 @@ "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); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); return 2; } /* * Close our references to the write handles that have now been inherited. @@ -447,14 +455,16 @@ */ return !(strstr(Out.buffer, "LNK1117") != NULL || strstr(Err.buffer, "LNK1117") != NULL || strstr(Out.buffer, "LNK4044") != NULL || - strstr(Err.buffer, "LNK4044") != NULL); + strstr(Err.buffer, "LNK4044") != NULL || + strstr(Out.buffer, "LNK4224") != NULL || + strstr(Err.buffer, "LNK4224") != NULL); } -DWORD WINAPI +static DWORD WINAPI ReadFromPipe( LPVOID args) { pipeinfo *pi = (pipeinfo *) args; char *lastBuf = pi->buffer; @@ -475,86 +485,30 @@ goto again; return 0; /* makes the compiler happy */ } -int +static 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 * +static const char * GetVersionFromFile( const char *filename, - const char *match) + const char *match, + int numdots) { size_t cbBuffer = 100; static char szBuffer[100]; char *szResult = NULL; FILE *fp = fopen(filename, "rt"); @@ -568,23 +522,25 @@ LPSTR p, q; p = strstr(szBuffer, match); if (p != NULL) { /* - * Skip to first digit. + * Skip to first digit after the match. */ + p += strlen(match); while (*p && !isdigit(*p)) { ++p; } /* * Find ending whitespace. */ q = p; - while (*q && (isalnum(*q) || *q == '.')) { + while (*q && (strchr("0123456789.ab", *q)) && ((!strchr(".ab", *q) + && (!strchr("ab", q[-1])) || --numdots))) { ++q; } memcpy(szBuffer, p, q - p); szBuffer[q-p] = 0; @@ -653,11 +609,11 @@ * @PACKAGE_NAME@ $(PACKAGE_NAME) * @PACKAGE_VERSION@ $(PACKAGE_VERSION) * << */ -int +static int SubstituteFile( const char *substitutions, const char *filename) { size_t cbBuffer = 1024; @@ -674,21 +630,21 @@ */ sp = fopen(substitutions, "rt"); if (sp != NULL) { while (fgets(szBuffer, cbBuffer, sp) != NULL) { - char *ks, *ke, *vs, *ve; - ks = szBuffer; + unsigned char *ks, *ke, *vs, *ve; + ks = (unsigned char*)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); + list_insert(&substPtr, (char*)ks, (char*)vs); } fclose(sp); } /* debug: dump the list */ @@ -699,15 +655,15 @@ 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) { @@ -723,11 +679,11 @@ memcpy(szBuffer, szCopy, sizeof(szCopy)); } } printf(szBuffer); } - + list_free(&substPtr); } fclose(fp); return 0; } @@ -738,11 +694,11 @@ * This composes the current working directory with a provided path * and returns the fully qualified and normalized path. * Mostly needed to setup paths for testing. */ -int +static int QualifyPath( const char *szPath) { char szCwd[MAX_PATH + 1]; char szTmp[MAX_PATH + 1]; @@ -753,10 +709,101 @@ PathCombine(szTmp, szCwd, szPath); PathCanonicalize(szCwd, szTmp); printf("%s\n", szCwd); return 0; } + +/* + * Implements LocateDependency for a single directory. See that command + * for an explanation. + * Returns 0 if found after printing the directory. + * Returns 1 if not found but no errors. + * Returns 2 on any kind of error + * Basically, these are used as exit codes for the process. + */ +static int LocateDependencyHelper(const char *dir, const char *keypath) +{ + HANDLE hSearch; + char path[MAX_PATH+1]; + int dirlen, keylen, ret; + WIN32_FIND_DATA finfo; + + if (dir == NULL || keypath == NULL) + return 2; /* Have no real error reporting mechanism into nmake */ + dirlen = strlen(dir); + if ((dirlen + 3) > sizeof(path)) + return 2; + strncpy(path, dir, dirlen); + strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */ + keylen = strlen(keypath); + +#if 0 /* This function is not available in Visual C++ 6 */ + /* + * Use numerics 0 -> FindExInfoStandard, + * 1 -> FindExSearchLimitToDirectories, + * as these are not defined in Visual C++ 6 + */ + hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0); +#else + hSearch = FindFirstFile(path, &finfo); +#endif + if (hSearch == INVALID_HANDLE_VALUE) + return 1; /* Not found */ + + /* Loop through all subdirs checking if the keypath is under there */ + ret = 1; /* Assume not found */ + do { + int sublen; + /* + * We need to check it is a directory despite the + * FindExSearchLimitToDirectories in the above call. See SDK docs + */ + if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + continue; + sublen = strlen(finfo.cFileName); + if ((dirlen+1+sublen+1+keylen+1) > sizeof(path)) + continue; /* Path does not fit, assume not matched */ + strncpy(path+dirlen+1, finfo.cFileName, sublen); + path[dirlen+1+sublen] = '\\'; + strncpy(path+dirlen+1+sublen+1, keypath, keylen+1); + if (PathFileExists(path)) { + /* Found a match, print to stdout */ + path[dirlen+1+sublen] = '\0'; + QualifyPath(path); + ret = 0; + break; + } + } while (FindNextFile(hSearch, &finfo)); + FindClose(hSearch); + return ret; +} + +/* + * LocateDependency -- + * + * Locates a dependency for a package. + * keypath - a relative path within the package directory + * that is used to confirm it is the correct directory. + * The search path for the package directory is currently only + * the parent and grandparent of the current working directory. + * If found, the command prints + * name_DIRPATH= + * and returns 0. If not found, does not print anything and returns 1. + */ +static int LocateDependency(const char *keypath) +{ + int i, ret; + static char *paths[] = {"..", "..\\..", "..\\..\\.."}; + + for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) { + ret = LocateDependencyHelper(paths[i], keypath); + if (ret == 0) + return ret; + } + return ret; +} + /* * Local variables: * mode: c * c-basic-offset: 4 ADDED undroid/DiffUtilTcl/win/rules-ext.vc Index: undroid/DiffUtilTcl/win/rules-ext.vc ================================================================== --- /dev/null +++ undroid/DiffUtilTcl/win/rules-ext.vc @@ -0,0 +1,118 @@ +# This file should only be included in makefiles for Tcl extensions, +# NOT in the makefile for Tcl itself. + +!ifndef _RULES_EXT_VC + +# We need to run from the directory the parent makefile is located in. +# nmake does not tell us what makefile was used to invoke it so parent +# makefile has to set the MAKEFILEVC macro or we just make a guess and +# warn if we think that is not the case. +!if "$(MAKEFILEVC)" == "" + +!if exist("$(PROJECT).vc") +MAKEFILEVC = $(PROJECT).vc +!elseif exist("makefile.vc") +MAKEFILEVC = makefile.vc +!endif +!endif # "$(MAKEFILEVC)" == "" + +!if !exist("$(MAKEFILEVC)") +MSG = ^ +You must run nmake from the directory containing the project makefile.^ +If you are doing that and getting this message, set the MAKEFILEVC^ +macro to the name of the project makefile. +!message WARNING: $(MSG) +!endif + +!if "$(PROJECT)" == "tcl" +!error The rules-ext.vc file is not intended for Tcl itself. +!endif + +# We extract version numbers using the nmakehlp program. For now use +# the local copy of nmakehlp. Once we locate Tcl, we will use that +# one if it is newer. +!if [$(CC) -nologo "nmakehlp.c" -link -subsystem:console > nul] +!endif + +# First locate the Tcl directory that we are working with. +!if "$(TCLDIR)" != "" + +_RULESDIR = $(TCLDIR:/=\) + +!else + +# If an installation path is specified, that is also the Tcl directory. +# Also Tk never builds against an installed Tcl, it needs Tcl sources +!if defined(INSTALLDIR) && "$(PROJECT)" != "tk" +_RULESDIR=$(INSTALLDIR:/=\) +!else +# Locate Tcl sources +!if [echo _RULESDIR = \> nmakehlp.out] \ + || [nmakehlp -L generic\tcl.h >> nmakehlp.out] +_RULESDIR = ..\..\tcl +!else +!include nmakehlp.out +!endif + +!endif # defined(INSTALLDIR).... + +!endif # ifndef TCLDIR + +# Now look for the targets.vc file under the Tcl root. Note we check this +# file and not rules.vc because the latter also exists on older systems. +!if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl +_RULESDIR = $(_RULESDIR)\lib\nmake +!elseif exist("$(_RULESDIR)\win\targets.vc") # Building against Tcl sources +_RULESDIR = $(_RULESDIR)\win +!else +# If we have not located Tcl's targets file, most likely we are compiling +# against an older version of Tcl and so must use our own support files. +_RULESDIR = . +!endif + +!if "$(_RULESDIR)" != "." +# Potentially using Tcl's support files. If this extension has its own +# nmake support files, need to compare the versions and pick newer. + +!if exist("rules.vc") # The extension has its own copy + +!if [echo TCL_RULES_MAJOR = \> versions.vc] \ + && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc] +!endif +!if [echo TCL_RULES_MINOR = \>> versions.vc] \ + && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc] +!endif + +!if [echo OUR_RULES_MAJOR = \>> versions.vc] \ + && [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc] +!endif +!if [echo OUR_RULES_MINOR = \>> versions.vc] \ + && [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc] +!endif +!include versions.vc +# We have a newer version of the support files, use them +!if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR)) +_RULESDIR = . +!endif + +!endif # if exist("rules.vc") + +!endif # if $(_RULESDIR) != "." + +# Let rules.vc know what copy of nmakehlp.c to use. +NMAKEHLPC = $(_RULESDIR)\nmakehlp.c + +# Get rid of our internal defines before calling rules.vc +!undef TCL_RULES_MAJOR +!undef TCL_RULES_MINOR +!undef OUR_RULES_MAJOR +!undef OUR_RULES_MINOR + +!if exist("$(_RULESDIR)\rules.vc") +!message *** Using $(_RULESDIR)\rules.vc +!include "$(_RULESDIR)\rules.vc" +!else +!error *** Could not locate rules.vc in $(_RULESDIR) +!endif + +!endif # _RULES_EXT_VC Index: undroid/DiffUtilTcl/win/rules.vc ================================================================== --- undroid/DiffUtilTcl/win/rules.vc +++ undroid/DiffUtilTcl/win/rules.vc @@ -1,320 +1,1000 @@ -#------------------------------------------------------------- -*- Makefile -*- +#------------------------------------------------------------- -*- makefile -*- # rules.vc -- # -# Microsoft Visual C++ makefile include for decoding the commandline -# macros. This file does not need editing to build Tcl. +# Part of the nmake based build system for Tcl and its extensions. +# This file does all the hard work in terms of parsing build options, +# compiler switches, defining common targets and macros. The Tcl makefile +# directly includes this. Extensions include it via "rules-ext.vc". # -# This version is modified from the Tcl source version to support -# building extensions using nmake. +# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for +# detailed documentation. # # 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) 2001-2003 David Gravereaux. # Copyright (c) 2003-2008 Patrick Thoyts -# -#------------------------------------------------------------------------------ -# RCS: @(#) $Id: rules.vc,v 1.9 2009/05/04 23:47:55 patthoyts Exp $ +# Copyright (c) 2017 Ashok P. Nadkarni #------------------------------------------------------------------------------ !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 +# The following macros define the version of the rules.vc nmake build system +# For modifications that are not backward-compatible, you *must* change +# the major version. +RULES_VERSION_MAJOR = 1 +RULES_VERSION_MINOR = 1 + +# The PROJECT macro must be defined by parent makefile. +!if "$(PROJECT)" == "" +!error *** Error: Macro PROJECT not defined! Please define it before including rules.vc +!endif + +!if "$(PRJ_PACKAGE_TCLNAME)" == "" +PRJ_PACKAGE_TCLNAME = $(PROJECT) +!endif + +# Also special case Tcl and Tk to save some typing later +DOING_TCL = 0 +DOING_TK = 0 +!if "$(PROJECT)" == "tcl" +DOING_TCL = 1 +!elseif "$(PROJECT)" == "tk" +DOING_TK = 1 +!endif + +!ifndef NEED_TK +# Backwards compatibility +!ifdef PROJECT_REQUIRES_TK +NEED_TK = $(PROJECT_REQUIRES_TK) +!else +NEED_TK = 0 +!endif +!endif + +!ifndef NEED_TCL_SOURCE +NEED_TCL_SOURCE = 0 +!endif + +!ifdef NEED_TK_SOURCE +!if $(NEED_TK_SOURCE) +NEED_TK = 1 +!endif +!else +NEED_TK_SOURCE = 0 +!endif + +################################################################ +# Nmake is a pretty weak environment in syntax and capabilities +# so this file is necessarily verbose. It's broken down into +# the following parts. +# +# 0. Sanity check that compiler environment is set up and initialize +# any built-in settings from the parent makefile +# 1. First define the external tools used for compiling, copying etc. +# as this is independent of everything else. +# 2. Figure out our build structure in terms of the directory, whether +# we are building Tcl or an extension, etc. +# 3. Determine the compiler and linker versions +# 4. Build the nmakehlp helper application +# 5. Determine the supported compiler options and features +# 6. Parse the OPTS macro value for user-specified build configuration +# 7. Parse the STATS macro value for statistics instrumentation +# 8. Parse the CHECKS macro for additional compilation checks +# 9. Extract Tcl, and possibly Tk, version numbers from the headers +# 10. Based on this selected configuration, construct the output +# directory and file paths +# 11. Construct the paths where the package is to be installed +# 12. Set up the actual options passed to compiler and linker based +# on the information gathered above. +# 13. Define some standard build targets and implicit rules. These may +# be optionally disabled by the parent makefile. +# 14. (For extensions only.) Compare the configuration of the target +# Tcl and the extensions and warn against discrepancies. +# +# One final note about the macro names used. They are as they are +# for historical reasons. We would like legacy extensions to +# continue to work with this make include file so be wary of +# changing them for consistency or clarity. + +# 0. Sanity check compiler environment + +# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or +# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir) + +!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR) +MSG = ^ +Visual C++ compiler environment not initialized. +!error $(MSG) +!endif + +# We need to run from the directory the parent makefile is located in. +# nmake does not tell us what makefile was used to invoke it so parent +# makefile has to set the MAKEFILEVC macro or we just make a guess and +# warn if we think that is not the case. +!if "$(MAKEFILEVC)" == "" + +!if exist("$(PROJECT).vc") +MAKEFILEVC = $(PROJECT).vc +!elseif exist("makefile.vc") +MAKEFILEVC = makefile.vc +!endif +!endif # "$(MAKEFILEVC)" == "" + +!if !exist("$(MAKEFILEVC)") +MSG = ^ +You must run nmake from the directory containing the project makefile.^ +If you are doing that and getting this message, set the MAKEFILEVC^ +macro to the name of the project makefile. +!message WARNING: $(MSG) +!endif + + +################################################################ +# 1. Define external programs being used #---------------------------------------------------------- # 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 +CPYDIR = xcopy /e /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. -#---------------------------------------------------------- - +###################################################################### +# 2. Figure out our build environment in terms of what we're building. +# +# (a) Tcl itself +# (b) Tk +# (c) a Tcl extension using libraries/includes from an *installed* Tcl +# (d) a Tcl extension using libraries/includes from Tcl source directory +# +# This last is needed because some extensions still need +# some Tcl interfaces that are not publicly exposed. +# +# The fragment will set the following macros: +# ROOT - root of this module sources +# COMPATDIR - source directory that holds compatibility sources +# DOCDIR - source directory containing documentation files +# GENERICDIR - platform-independent source directory +# WINDIR - Windows-specific source directory +# TESTDIR - directory containing test files +# TOOLSDIR - directory containing build tools +# _TCLDIR - root of the Tcl installation OR the Tcl sources. Not set +# when building Tcl itself. +# _INSTALLDIR - native form of the installation path. For Tcl +# this will be the root of the Tcl installation. For extensions +# this will be the lib directory under the root. +# TCLINSTALL - set to 1 if _TCLDIR refers to +# headers and libraries from an installed Tcl, and 0 if built against +# Tcl sources. Not set when building Tcl itself. Yes, not very well +# named. +# _TCL_H - native path to the tcl.h file +# +# If Tk is involved, also sets the following +# _TKDIR - native form Tk installation OR Tk source. Not set if building +# Tk itself. +# TKINSTALL - set 1 if _TKDIR refers to installed Tk and 0 if Tk sources +# _TK_H - native path to the tk.h file + +# Root directory for sources and assumed subdirectories +ROOT = $(MAKEDIR)\.. +# The following paths CANNOT have spaces in them as they appear on the +# left side of implicit rules. +!ifndef COMPATDIR +COMPATDIR = $(ROOT)\compat +!endif +!ifndef DOCDIR +DOCDIR = $(ROOT)\doc +!endif +!ifndef GENERICDIR +GENERICDIR = $(ROOT)\generic +!endif +!ifndef TOOLSDIR +TOOLSDIR = $(ROOT)\tools +!endif +!ifndef TESTDIR +TESTDIR = $(ROOT)\tests +!endif +!ifndef LIBDIR +!if exist("$(ROOT)\library") +LIBDIR = $(ROOT)\library +!else +LIBDIR = $(ROOT)\lib +!endif +!endif +!ifndef DEMODIR +!if exist("$(LIBDIR)\demos") +DEMODIR = $(LIBDIR)\demos +!else +DEMODIR = $(ROOT)\demos +!endif +!endif # ifndef DEMODIR +# Do NOT enclose WINDIR in a !ifndef because Windows always defines +# WINDIR env var to point to c:\windows! +# TBD - This is a potentially dangerous conflict, rename WINDIR to +# something else +WINDIR = $(ROOT)\win + +!ifndef RCDIR +!if exist("$(WINDIR)\rc") +RCDIR = $(WINDIR)\rc +!else +RCDIR = $(WINDIR) +!endif +!endif +RCDIR = $(RCDIR:/=\) + +# The target directory where the built packages and binaries will be installed. +# INSTALLDIR is the (optional) path specified by the user. +# _INSTALLDIR is INSTALLDIR using the backslash separator syntax +!ifdef INSTALLDIR +### Fix the path separators. +_INSTALLDIR = $(INSTALLDIR:/=\) +!else +### Assume the normal default. +_INSTALLDIR = $(HOMEDRIVE)\Tcl +!endif + +!if $(DOING_TCL) + +# BEGIN Case 2(a) - Building Tcl itself + +# Only need to define _TCL_H +_TCL_H = ..\generic\tcl.h + +# END Case 2(a) - Building Tcl itself + +!elseif $(DOING_TK) + +# BEGIN Case 2(b) - Building Tk + +TCLINSTALL = 0 # Tk always builds against Tcl source, not an installed Tcl +!if "$(TCLDIR)" == "" +!if [echo TCLDIR = \> nmakehlp.out] \ + || [nmakehlp -L generic\tcl.h >> nmakehlp.out] +!error *** Could not locate Tcl source directory. +!endif +!include nmakehlp.out +!endif # TCLDIR == "" + +_TCLDIR = $(TCLDIR:/=\) +_TCL_H = $(_TCLDIR)\generic\tcl.h +!if !exist("$(_TCL_H)") +!error Could not locate tcl.h. Please set the TCLDIR macro to point to the Tcl *source* directory. +!endif + +_TK_H = ..\generic\tk.h + +# END Case 2(b) - Building Tk + +!else + +# BEGIN Case 2(c) or (d) - Building an extension other than Tk + +# If command line has specified Tcl location through TCLDIR, use it +# else default to the INSTALLDIR setting +!if "$(TCLDIR)" != "" + +_TCLDIR = $(TCLDIR:/=\) +!if exist("$(_TCLDIR)\include\tcl.h") # Case 2(c) with TCLDIR defined +TCLINSTALL = 1 +_TCL_H = $(_TCLDIR)\include\tcl.h +!elseif exist("$(_TCLDIR)\generic\tcl.h") # Case 2(d) with TCLDIR defined +TCLINSTALL = 0 +_TCL_H = $(_TCLDIR)\generic\tcl.h +!endif + +!else # # Case 2(c) for extensions with TCLDIR undefined + +# Need to locate Tcl depending on whether it needs Tcl source or not. +# If we don't, check the INSTALLDIR for an installed Tcl first + +!if exist("$(_INSTALLDIR)\include\tcl.h") && !$(NEED_TCL_SOURCE) + +TCLINSTALL = 1 +TCLDIR = $(_INSTALLDIR)\.. +# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions +# later so the \.. accounts for the /lib +_TCLDIR = $(_INSTALLDIR)\.. +_TCL_H = $(_TCLDIR)\include\tcl.h + +!else # exist(...) && ! $(NEED_TCL_SOURCE) + +!if [echo _TCLDIR = \> nmakehlp.out] \ + || [nmakehlp -L generic\tcl.h >> nmakehlp.out] +!error *** Could not locate Tcl source directory. +!endif +!include nmakehlp.out +TCLINSTALL = 0 +TCLDIR = $(_TCLDIR) +_TCL_H = $(_TCLDIR)\generic\tcl.h + +!endif # exist(...) && ! $(NEED_TCL_SOURCE) + +!endif # TCLDIR + +!ifndef _TCL_H +MSG =^ +Failed to find tcl.h. The TCLDIR macro is set incorrectly or is not set and default path does not contain tcl.h. +!error $(MSG) +!endif + +# Now do the same to locate Tk headers and libs if project requires Tk +!if $(NEED_TK) + +!if "$(TKDIR)" != "" + +_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 +!endif + +!else # TKDIR not defined + +# Need to locate Tcl depending on whether it needs Tcl source or not. +# If we don't, check the INSTALLDIR for an installed Tcl first + +!if exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE) + +TKINSTALL = 1 +# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions +# later so the \.. accounts for the /lib +_TKDIR = $(_INSTALLDIR)\.. +_TK_H = $(_TKDIR)\include\tk.h +TKDIR = $(_TKDIR) + +!else # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE) + +!if [echo _TKDIR = \> nmakehlp.out] \ + || [nmakehlp -L generic\tk.h >> nmakehlp.out] +!error *** Could not locate Tk source directory. +!endif +!include nmakehlp.out +TKINSTALL = 0 +TKDIR = $(_TKDIR) +_TK_H = $(_TKDIR)\generic\tk.h + +!endif # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE) + +!endif # TKDIR + +!ifndef _TK_H +MSG =^ +Failed to find tk.h. The TKDIR macro is set incorrectly or is not set and default path does not contain tk.h. +!error $(MSG) +!endif + +!endif # NEED_TK + +!if $(NEED_TCL_SOURCE) && $(TCLINSTALL) +MSG = ^ +*** Warning: This extension requires the source distribution of Tcl.^ +*** Please set the TCLDIR macro to point to the Tcl sources. +!error $(MSG) +!endif + +!if $(NEED_TK_SOURCE) +!if $(TKINSTALL) +MSG = ^ +*** Warning: This extension requires the source distribution of Tk.^ +*** Please set the TKDIR macro to point to the Tk sources. +!error $(MSG) +!endif +!endif + + +# If INSTALLDIR set to tcl installation root dir then reset to the +# lib dir for installing extensions +!if exist("$(_INSTALLDIR)\include\tcl.h") +_INSTALLDIR=$(_INSTALLDIR)\lib +!endif + +# END Case 2(c) or (d) - Building an extension +!endif # if $(DOING_TCL) + +################################################################ +# 3. Determine compiler version and architecture +# In this section, we figure out the compiler version and the +# architecture for which we are building. This sets the +# following macros: +# VCVERSION - the internal compiler version as 1200, 1400, 1910 etc. +# This is also printed by the compiler in dotted form 19.10 etc. +# VCVER - the "marketing version", for example Visual C++ 6 for internal +# compiler version 1200. This is kept only for legacy reasons as it +# does not make sense for recent Microsoft compilers. Only used for +# output directory names. +# ARCH - set to IX86 or AMD64 depending on 32- or 64-bit target +# NATIVE_ARCH - set to IX86 or AMD64 for the host machine +# MACHINE - same as $(ARCH) - legacy +# _VC_MANIFEST_EMBED_{DLL,EXE} - commands for embedding a manifest if needed +# CFG_ENCODING - set to an character encoding. +# TBD - this is passed to compiler as TCL_CFGVAL_ENCODING but can't +# see where it is used + +cc32 = $(CC) # built-in default. +link32 = link +lib32 = lib +rc32 = $(RC) # built-in default. + +#---------------------------------------------------------------- +# Figure out the compiler architecture and version by writing +# the C macros to a file, preprocessing them with the C +# preprocessor and reading back the created file + +_HASH=^# _VC_MANIFEST_EMBED_EXE= _VC_MANIFEST_EMBED_DLL= VCVER=0 !if ![echo VCVERSION=_MSC_VER > vercl.x] \ - && ![cl -nologo -TC -P vercl.x $(ERRNULL)] + && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \ + && ![echo ARCH=IX86 >> vercl.x] \ + && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \ + && ![echo ARCH=AMD64 >> vercl.x] \ + && ![echo $(_HASH)endif >> vercl.x] \ + && ![$(cc32) -nologo -TC -P vercl.x 2>NUL] !include vercl.i -!if $(VCVERSION) >= 1500 -VCVER=9 -!elseif $(VCVERSION) >= 1400 -VCVER=8 -!elseif $(VCVERSION) >= 1300 -VCVER=7 -!elseif $(VCVERSION) >= 1200 -VCVER=6 +!if $(VCVERSION) < 1900 +!if ![echo VCVER= ^\> vercl.vc] \ + && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc] +!include vercl.vc !endif +!else +# The simple calculation above does not apply to new Visual Studio releases +# Keep the compiler version in its native form. +VCVER = $(VCVERSION) +!endif +!endif + +!if ![del 2>NUL /q/f vercl.x vercl.i vercl.vc] +!endif + +#---------------------------------------------------------------- +# The MACHINE macro is used by legacy makefiles so set it as well +!ifdef MACHINE +!if "$(MACHINE)" == "x86" +!undef MACHINE +MACHINE = IX86 +!elseif "$(MACHINE)" == "x64" +!undef MACHINE +MACHINE = AMD64 +!endif +!if "$(MACHINE)" != "$(ARCH)" +!error Specified MACHINE macro $(MACHINE) does not match detected target architecture $(ARCH). +!endif +!else +MACHINE=$(ARCH) +!endif + +#------------------------------------------------------------ +# Figure out the *host* architecture by reading the registry + +!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86] +NATIVE_ARCH=IX86 +!else +NATIVE_ARCH=AMD64 !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. -#---------------------------------------------------------- +!ifndef CFG_ENCODING +CFG_ENCODING = \"cp1252\" +!endif -!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"] +################################################################ +# 4. Build the nmakehlp program +# This is a helper app we need to overcome nmake's limiting +# environment. We will call out to it to get various bits of +# information about supported compiler options etc. +# +# Tcl itself will always use the nmakehlp.c program which is +# in its own source. This is the "master" copy and kept updated. +# +# Extensions built against an installed Tcl will use the installed +# copy of Tcl's nmakehlp.c if there is one and their own version +# otherwise. In the latter case, they would also be using their own +# rules.vc. Note that older versions of Tcl do not install nmakehlp.c +# or rules.vc. +# +# Extensions built against Tcl sources will use the one from the Tcl source. +# +# When building an extension using a sufficiently new version of Tcl, +# rules-ext.vc will define NMAKEHLPC appropriately to point to the +# copy of nmakehlp.c to be used. + +!ifndef NMAKEHLPC +# Default to the one in the current directory (the extension's own nmakehlp.c) +NMAKEHLPC = nmakehlp.c + +!if !$(DOING_TCL) +!if $(TCLINSTALL) +!if exist("$(_TCLDIR)\lib\nmake\nmakehlp.c") +NMAKEHLPC = $(_TCLDIR)\lib\nmake\nmakehlp.c +!endif +!else # ! $(TCLINSTALL) +!if exist("$(_TCLDIR)\win\nmakehlp.c") +NMAKEHLPC = $(_TCLDIR)\win\nmakehlp.c +!endif +!endif # $(TCLINSTALL) +!endif # !$(DOING_TCL) + +!endif # NMAKEHLPC + +# We always build nmakehlp even if it exists since we do not know +# what source it was built from. +!if [$(cc32) -nologo "$(NMAKEHLPC)" -link -subsystem:console > nul] +!endif + +################################################################ +# 5. Test for compiler features +# Visual C++ compiler options have changed over the years. Check +# which options are supported by the compiler in use. +# +# The following macros are set: +# OPTIMIZATIONS - the compiler flags to be used for optimized builds +# DEBUGFLAGS - the compiler flags to be used for debug builds +# LINKERFLAGS - Flags passed to the linker +# +# Note that these are the compiler settings *available*, not those +# that will be *used*. The latter depends on the OPTS macro settings +# which we have not yet parsed. +# +# Also note that some of the flags in OPTIMIZATIONS are not really +# related to optimization. They are placed there only for legacy reasons +# as some extensions expect them to be included in that macro. + +# -Op improves float consistency. Note only needed for older compilers +# Newer compilers do not need or support this option. +!if [nmakehlp -c -Op] +FPOPTS = -Op +!endif + +# Strict floating point semantics - present in newer compilers in lieu of -Op +!if [nmakehlp -c -fp:strict] +FPOPTS = $(FPOPTS) -fp:strict +!endif + +!if "$(MACHINE)" == "IX86" +### test for pentium errata +!if [nmakehlp -c -QI0f] +!message *** Compiler has 'Pentium 0x0f fix' +FPOPTS = $(FPOPTS) -QI0f +!else +!message *** Compiler does not have 'Pentium 0x0f fix' +!endif +!endif + +### test for optimizations +# /O2 optimization includes /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy as per +# documentation. Note we do NOT want /Gs as that inserts a _chkstk +# stack probe at *every* function entry, not just those with more than +# a page of stack allocation resulting in a performance hit. However, +# /O2 documentation is misleading as its stack probes are simply the +# default page size locals allocation probes and not what is implied +# by an explicit /Gs option. + +OPTIMIZATIONS = $(FPOPTS) + +!if [nmakehlp -c -O2] +OPTIMIZING = 1 +OPTIMIZATIONS = $(OPTIMIZATIONS) -O2 +!else +# Legacy, really. All modern compilers support this +!message *** Compiler does not have 'Optimizations' +OPTIMIZING = 0 +!endif + +# Checks for buffer overflows in local arrays +!if [nmakehlp -c -GS] +OPTIMIZATIONS = $(OPTIMIZATIONS) -GS +!endif + +# Link time optimization. Note that this option (potentially) makes +# generated libraries only usable by the specific VC++ version that +# created it. Requires /LTCG linker option +!if [nmakehlp -c -GL] +OPTIMIZATIONS = $(OPTIMIZATIONS) -GL +CC_GL_OPT_ENABLED = 1 +!else +# In newer compilers -GL and -YX are incompatible. +!if [nmakehlp -c -YX] +OPTIMIZATIONS = $(OPTIMIZATIONS) -YX +!endif +!endif # [nmakehlp -c -GL] + +DEBUGFLAGS = $(FPOPTS) + +# Run time error checks. Not available or valid in a release, non-debug build +# RTC is for modern compilers, -GZ is legacy +!if [nmakehlp -c -RTC1] +DEBUGFLAGS = $(DEBUGFLAGS) -RTC1 +!elseif [nmakehlp -c -GZ] +DEBUGFLAGS = $(DEBUGFLAGS) -GZ +!endif + +#---------------------------------------------------------------- +# Linker flags + +# LINKER_TESTFLAGS are for internal use when we call nmakehlp to test +# if the linker supports a specific option. Without these flags link will +# return "LNK1561: entry point must be defined" error compiling from VS-IDE: +# They are not passed through to the actual application / extension +# link rules. +!ifndef LINKER_TESTFLAGS +LINKER_TESTFLAGS = /DLL /NOENTRY /OUT:nmakehlp.out +!endif + +LINKERFLAGS = + +# If compiler has enabled link time optimization, linker must too with -ltcg +!ifdef CC_GL_OPT_ENABLED +!if [nmakehlp -l -ltcg $(LINKER_TESTFLAGS)] +LINKERFLAGS = $(LINKERFLAGS) -ltcg +!endif +!endif + +######################################################################## +# 6. Parse the OPTS macro to work out the requested build configuration. +# Based on this, we will construct the actual switches to be passed to the +# compiler and linker using the macros defined in the previous section. +# The following macros are defined by this section based on OPTS +# STATIC_BUILD - 0 -> Tcl is to be built as a shared library +# 1 -> build as a static library and shell +# TCL_THREADS - legacy but always 1 on Windows since winsock requires it. +# DEBUG - 1 -> debug build, 0 -> release builds +# SYMBOLS - 1 -> generate PDB's, 0 -> no PDB's +# PROFILE - 1 -> generate profiling info, 0 -> no profiling +# PGO - 1 -> profile based optimization, 0 -> no +# MSVCRT - 1 -> link to dynamic C runtime even when building static Tcl build +# 0 -> link to static C runtime for static Tcl build. +# Does not impact shared Tcl builds (STATIC_BUILD == 0) +# TCL_USE_STATIC_PACKAGES - 1 -> statically link the registry and dde extensions +# in the Tcl shell. 0 -> keep them as shared libraries +# Does not impact shared Tcl builds. +# USE_THREAD_ALLOC - 1 -> Use a shared global free pool for allocation. +# 0 -> Use the non-thread allocator. +# UNCHECKED - 1 -> when doing a debug build with symbols, use the release +# C runtime, 0 -> use the debug C runtime. +# USE_STUBS - 1 -> compile to use stubs interfaces, 0 -> direct linking +# CONFIG_CHECK - 1 -> check current build configuration against Tcl +# configuration (ignored for Tcl itself) +# Further, LINKERFLAGS are modified based on above. + +# Default values for all the above STATIC_BUILD = 0 TCL_THREADS = 1 DEBUG = 0 +SYMBOLS = 0 PROFILE = 0 -MSVCRT = 0 -LOIMPACT = 0 +PGO = 0 +MSVCRT = 1 TCL_USE_STATIC_PACKAGES = 0 USE_THREAD_ALLOC = 1 -USE_THREAD_STORAGE = 1 -UNCHECKED = 0 +UNCHECKED = 0 +CONFIG_CHECK = 1 +!if $(DOING_TCL) +USE_STUBS = 0 !else +USE_STUBS = 1 +!endif + +# If OPTS is not empty AND does not contain "none" which turns off all OPTS +# set the above macros based on OPTS content +!if "$(OPTS)" != "" && ![nmakehlp -f "$(OPTS)" "none"] + +# OPTS are specified, parse them + !if [nmakehlp -f $(OPTS) "static"] !message *** Doing static STATIC_BUILD = 1 +!endif + +!if [nmakehlp -f $(OPTS) "nostubs"] +!message *** Not using stubs +USE_STUBS = 0 +!endif + +!if [nmakehlp -f $(OPTS) "nomsvcrt"] +!message *** Doing nomsvcrt +MSVCRT = 0 !else -STATIC_BUILD = 0 -!endif !if [nmakehlp -f $(OPTS) "msvcrt"] !message *** Doing msvcrt MSVCRT = 1 !else +!if !$(STATIC_BUILD) +MSVCRT = 1 +!else MSVCRT = 0 !endif -!if [nmakehlp -f $(OPTS) "staticpkg"] +!endif +!endif # [nmakehlp -f $(OPTS) "nomsvcrt"] + +!if [nmakehlp -f $(OPTS) "staticpkg"] && $(STATIC_BUILD) !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 +USE_THREAD_ALLOC= 0 !else -TCL_THREADS = 1 +TCL_THREADS = 1 +USE_THREAD_ALLOC= 1 !endif + !if [nmakehlp -f $(OPTS) "symbols"] !message *** Doing symbols DEBUG = 1 !else DEBUG = 0 !endif + +!if [nmakehlp -f $(OPTS) "pdbs"] +!message *** Doing pdbs +SYMBOLS = 1 +!else +SYMBOLS = 0 +!endif + !if [nmakehlp -f $(OPTS) "profile"] !message *** Doing profile PROFILE = 1 !else PROFILE = 0 !endif + +!if [nmakehlp -f $(OPTS) "pgi"] +!message *** Doing profile guided optimization instrumentation +PGO = 1 +!elseif [nmakehlp -f $(OPTS) "pgo"] +!message *** Doing profile guided optimization +PGO = 2 +!else +PGO = 0 +!endif + !if [nmakehlp -f $(OPTS) "loimpact"] -!message *** Doing loimpact -LOIMPACT = 1 -!else -LOIMPACT = 0 +!message *** Warning: ignoring option "loimpact" - deprecated on modern Windows. !endif + +# TBD - should get rid of this option !if [nmakehlp -f $(OPTS) "thrdalloc"] !message *** Doing thrdalloc USE_THREAD_ALLOC = 1 -!else +!endif + +!if [nmakehlp -f $(OPTS) "tclalloc"] 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 + +!if [nmakehlp -f $(OPTS) "noconfigcheck"] +CONFIG_CHECK = 1 +!else +CONFIG_CHECK = 0 !endif +!endif # "$(OPTS)" != "" && ... parsing of OPTS -!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 +# Set linker flags based on above + +!if $(PGO) > 1 +!if [nmakehlp -l -ltcg:pgoptimize $(LINKER_TESTFLAGS)] +LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize +!else +MSG=^ +This compiler does not support profile guided optimization. +!error $(MSG) +!endif +!elseif $(PGO) > 0 +!if [nmakehlp -l -ltcg:pginstrument $(LINKER_TESTFLAGS)] +LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument +!else +MSG=^ +This compiler does not support profile guided optimization. +!error $(MSG) +!endif !endif +################################################################ +# 7. Parse the STATS macro to configure code instrumentation +# The following macros are set by this section: +# TCL_MEM_DEBUG - 1 -> enables memory allocation instrumentation +# 0 -> disables +# TCL_COMPILE_DEBUG - 1 -> enables byte compiler logging +# 0 -> disables -#---------------------------------------------------------- +# Default both are off +TCL_MEM_DEBUG = 0 +TCL_COMPILE_DEBUG = 0 + +!if "$(STATS)" != "" && ![nmakehlp -f "$(STATS)" "none"] + +!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 + +#################################################################### +# 8. Parse the CHECKS macro to configure additional compiler checks +# The following macros are set by this section: +# WARNINGS - compiler switches that control the warnings level +# TCL_NO_DEPRECATED - 1 -> disable support for deprecated functions +# 0 -> enable deprecated functions + +# Defaults - Permit deprecated functions and warning level 3 +TCL_NO_DEPRECATED = 0 +WARNINGS = -W3 + +!if "$(CHECKS)" != "" && ![nmakehlp -f "$(CHECKS)" "none"] + +!if [nmakehlp -f $(CHECKS) "nodep"] +!message *** Doing nodep check +TCL_NO_DEPRECATED = 1 +!endif + +!if [nmakehlp -f $(CHECKS) "fullwarn"] +!message *** Doing full warnings check +WARNINGS = -W4 +!if [nmakehlp -l -warn:3 $(LINKER_TESTFLAGS)] +LINKERFLAGS = $(LINKERFLAGS) -warn:3 +!endif +!endif + +!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64] +!message *** Doing 64bit portability warnings +WARNINGS = $(WARNINGS) -Wp64 +!endif + +!endif + +################################################################ +# 9. Extract various version numbers +# For Tcl and Tk, version numbers are extracted from tcl.h and tk.h +# respectively. For extensions, versions are extracted from the +# configure.in or configure.ac from the TEA configuration if it +# exists, and unset otherwise. +# Sets the following macros: +# TCL_MAJOR_VERSION +# TCL_MINOR_VERSION +# TCL_PATCH_LEVEL +# TCL_VERSION +# TK_MAJOR_VERSION +# TK_MINOR_VERSION +# TK_PATCH_LEVEL +# TK_VERSION +# DOTVERSION - set as (for example) 2.5 +# VERSION - set as (for example 25) +#-------------------------------------------------------------- + +!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 defined(_TK_H) +!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 +!endif # _TK_H + +!include versions.vc + +TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION) +TCL_DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION) +!if defined(_TK_H) +TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION) +TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION) +!endif + +# Set DOTVERSION and VERSION +!if $(DOING_TCL) + +DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION) +VERSION = $(TCL_VERSION) + +!elseif $(DOING_TK) + +DOTVERSION = $(TK_DOTVERSION) +VERSION = $(TK_VERSION) + +!else # Doing a non-Tk extension + +# If parent makefile has not defined DOTVERSION, try to get it from TEA +# first from a configure.in file, and then from configure.ac +!ifndef DOTVERSION +!if [echo DOTVERSION = \> versions.vc] \ + || [nmakehlp -V $(ROOT)\configure.in ^[$(PROJECT)^] >> versions.vc] +!if [echo DOTVERSION = \> versions.vc] \ + || [nmakehlp -V $(ROOT)\configure.ac ^[$(PROJECT)^] >> versions.vc] +!error *** Could not figure out extension version. Please define DOTVERSION in parent makefile before including rules.vc. +!endif +!endif +!include versions.vc +!endif # DOTVERSION +VERSION = $(DOTVERSION:.=) + +!endif # $(DOING_TCL) ... etc. + +################################################################ +# 10. Construct output directory and file paths # 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: +# In order to avoid inadvertent mixing of object files built using +# different compilers, build configurations etc., +# +# Naming convention (suffixes): # 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 +# 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. +# +# The following macros are set in this section: +# SUFX - the suffix to use for binaries based on above naming convention +# BUILDDIRTOP - the toplevel default output directory +# is of the form {Release,Debug}[_AMD64][_COMPILERVERSION] +# TMP_DIR - directory where object files are created +# OUT_DIR - directory where output executables are created +# Both TMP_DIR and OUT_DIR are defaulted only if not defined by the +# parent makefile (or command line). The default values are +# based on BUILDDIRTOP. +# STUBPREFIX - name of the stubs library for this project +# PRJIMPLIB - output path of the generated project import library +# PRJLIBNAME - name of generated project library +# PRJLIB - output path of generated project library +# PRJSTUBLIBNAME - name of the generated project stubs library +# PRJSTUBLIB - output path of the generated project stubs library +# RESFILE - output resource file (only if not static build) + +SUFX = tsgx !if $(DEBUG) BUILDDIRTOP = Debug !else BUILDDIRTOP = Release @@ -335,14 +1015,12 @@ !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=) @@ -364,66 +1042,221 @@ !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. -#---------------------------------------------------------- +# Relative paths -> absolute +!if [echo OUT_DIR = \> nmakehlp.out] \ + || [nmakehlp -Q "$(OUT_DIR)" >> nmakehlp.out] +!error *** Could not fully qualify path OUT_DIR=$(OUT_DIR) +!endif +!if [echo TMP_DIR = \>> nmakehlp.out] \ + || [nmakehlp -Q "$(TMP_DIR)" >> nmakehlp.out] +!error *** Could not fully qualify path TMP_DIR=$(TMP_DIR) +!endif +!include nmakehlp.out + +# The name of the stubs library for the project being built +STUBPREFIX = $(PROJECT)stub + +# Set up paths to various Tcl executables and libraries needed by extensions +!if $(DOING_TCL) + +TCLSHNAME = $(PROJECT)sh$(TCL_VERSION)$(SUFX).exe +TCLSH = $(OUT_DIR)\$(TCLSHNAME) +TCLIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib +TCLLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT) +TCLLIB = $(OUT_DIR)\$(TCLLIBNAME) + +TCLSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib +TCLSTUBLIB = $(OUT_DIR)\$(TCLSTUBLIBNAME) +TCL_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)" + +!else # ! $(DOING_TCL) + +!if $(TCLINSTALL) # Building against an installed Tcl + +# When building extensions, we need to locate tclsh. Depending on version +# of Tcl we are building against, this may or may not have a "t" suffix. +# Try various possibilities in turn. +TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe +!if !exist("$(TCLSH)") && $(TCL_THREADS) +TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe +!endif +!if !exist("$(TCLSH)") +TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX:t=).exe +!endif + +TCLSTUBLIB = $(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib +TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib +# When building extensions, may be linking against Tcl that does not add +# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility. +!if !exist("$(TCLIMPLIB)") +TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX:t=).lib +!endif +TCL_LIBRARY = $(_TCLDIR)\lib +TCLREGLIB = $(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib +TCLDDELIB = $(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib +TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target +TCL_INCLUDES = -I"$(_TCLDIR)\include" + +!else # Building against Tcl sources + +TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe +!if !exist($(TCLSH)) && $(TCL_THREADS) +TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe +!endif +!if !exist($(TCLSH)) +TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX:t=).exe +!endif +TCLSTUBLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib +TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib +# When building extensions, may be linking against Tcl that does not add +# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility. +!if !exist("$(TCLIMPLIB)") +TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX:t=).lib +!endif +TCL_LIBRARY = $(_TCLDIR)\library +TCLREGLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib +TCLDDELIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib +TCLTOOLSDIR = $(_TCLDIR)\tools +TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win" + +!endif # TCLINSTALL + +tcllibs = "$(TCLSTUBLIB)" "$(TCLIMPLIB)" + +!endif # $(DOING_TCL) + +# We need a tclsh that will run on the host machine as part of the build. +# IX86 runs on all architectures. +!ifndef TCLSH_NATIVE +!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)" +TCLSH_NATIVE = $(TCLSH) +!else +!error You must explicitly set TCLSH_NATIVE for cross-compilation +!endif +!endif + +# Do the same for Tk and Tk extensions that require the Tk libraries +!if $(DOING_TK) || $(NEED_TK) +WISHNAMEPREFIX = wish +WISHNAME = $(WISHNAMEPREFIX)$(TK_VERSION)$(SUFX).exe +TKLIBNAME = $(PROJECT)$(TK_VERSION)$(SUFX).$(EXT) +TKSTUBLIBNAME = tkstub$(TK_VERSION).lib +TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX).lib + +!if $(DOING_TK) +WISH = $(OUT_DIR)\$(WISHNAME) +TKSTUBLIB = $(OUT_DIR)\$(TKSTUBLIBNAME) +TKIMPLIB = $(OUT_DIR)\$(TKIMPLIBNAME) +TKLIB = $(OUT_DIR)\$(TKLIBNAME) +TK_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)" + +!else # effectively NEED_TK + +!if $(TKINSTALL) # Building against installed Tk +WISH = $(_TKDIR)\bin\$(WISHNAME) +TKSTUBLIB = $(_TKDIR)\lib\$(TKSTUBLIBNAME) +TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME) +# When building extensions, may be linking against Tk that does not add +# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility. +!if !exist("$(TKIMPLIB)") +TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib +TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME) +!endif +TK_INCLUDES = -I"$(_TKDIR)\include" +!else # Building against Tk sources +WISH = $(_TKDIR)\win\$(BUILDDIRTOP)\$(WISHNAME) +TKSTUBLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKSTUBLIBNAME) +TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME) +# When building extensions, may be linking against Tk that does not add +# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility. +!if !exist("$(TKIMPLIB)") +TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib +TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME) +!endif +TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib" +!endif # TKINSTALL +tklibs = "$(TKSTUBLIB)" "$(TKIMPLIB)" + +!endif # $(DOING_TK) +!endif # $(DOING_TK) || $(NEED_TK) + +# Various output paths +PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib +PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT) +PRJLIB = $(OUT_DIR)\$(PRJLIBNAME) + +PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib +PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME) + +# If extension parent makefile has not defined a resource definition file, +# we will generate one from standard template. +!if !$(DOING_TCL) && !$(DOING_TK) && !$(STATIC_BUILD) +!ifdef RCFILE +RESFILE = $(TMP_DIR)\$(RCFILE:.rc=.res) +!else +RESFILE = $(TMP_DIR)\$(PROJECT).res +!endif +!endif + +################################################################### +# 11. Construct the paths for the installation directories +# The following macros get defined in this section: +# LIB_INSTALL_DIR - where libraries should be installed +# BIN_INSTALL_DIR - where the executables should be installed +# DOC_INSTALL_DIR - where documentation should be installed +# SCRIPT_INSTALL_DIR - where scripts should be installed +# INCLUDE_INSTALL_DIR - where C include files should be installed +# DEMO_INSTALL_DIR - where demos should be installed +# PRJ_INSTALL_DIR - where package will be installed (not set for tcl and tk) + +!if $(DOING_TCL) || $(DOING_TK) +LIB_INSTALL_DIR = $(_INSTALLDIR)\lib +BIN_INSTALL_DIR = $(_INSTALLDIR)\bin +DOC_INSTALL_DIR = $(_INSTALLDIR)\doc +!if $(DOING_TCL) +SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION) +!else # DOING_TK +SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TK_MAJOR_VERSION).$(TK_MINOR_VERSION) +!endif +DEMO_INSTALL_DIR = $(SCRIPT_INSTALL_DIR)\demos +INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\include + +!else # extension other than Tk + +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) +DEMO_INSTALL_DIR = $(PRJ_INSTALL_DIR)\demos +INCLUDE_INSTALL_DIR = $(_TCLDIR)\include + +!endif + +################################################################### +# 12. Set up actual options to be passed to the compiler and linker +# Now we have all the information we need, set up the actual flags and +# options that we will pass to the compiler and linker. The main +# makefile should use these in combination with whatever other flags +# and switches are specific to it. +# The following macros are defined, names are for historical compatibility: +# OPTDEFINES - /Dxxx C macro flags based on user-specified OPTS +# COMPILERFLAGS - /Dxxx C macro flags independent of any configuration opttions +# crt - Compiler switch that selects the appropriate C runtime +# cdebug - Compiler switches related to debug AND optimizations +# cwarn - Compiler switches that set warning levels +# cflags - complete compiler switches (subsumes cdebug and cwarn) +# ldebug - Linker switches controlling debug information and optimization +# lflags - complete linker switches (subsumes ldebug) except subsystem type +# dlllflags - complete linker switches to build DLLs (subsumes lflags) +# conlflags - complete linker switches for console program (subsumes lflags) +# guilflags - complete linker switches for GUI program (subsumes lflags) +# baselibs - minimum Windows libraries required. Parent makefile can +# define PRJ_LIBS before including rules.rc if additional libs are needed OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS !if $(TCL_MEM_DEBUG) OPTDEFINES = $(OPTDEFINES) -DTCL_MEM_DEBUG @@ -434,189 +1267,486 @@ !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) +!if $(USE_STUBS) +# Note we do not define USE_TCL_STUBS even when building tk since some +# test targets in tk do not use stubs +!if ! $(DOING_TCL) +USE_STUBS_DEFS = -DUSE_TCL_STUBS -DUSE_TCLOO_STUBS +!if $(NEED_TK) +USE_STUBS_DEFS = $(USE_STUBS_DEFS) -DUSE_TK_STUBS +!endif +!endif +!endif # USE_STUBS + +!if !$(DEBUG) +OPTDEFINES = $(OPTDEFINES) -DNDEBUG +!if $(OPTIMIZING) OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED +!endif !endif !if $(PROFILE) OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED !endif -!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64" -OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT -!endif - - -#---------------------------------------------------------- -# Get common info used when building extensions. -#---------------------------------------------------------- - -!if "$(PROJECT)" != "tcl" - -# 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 - -!if [echo REM = This file is generated from rules.vc > version.vc] -!endif -!if exist("$(_TCL_H)") -!if [echo TCL_DOTVERSION = \>> version.vc] \ - && [nmakehlp -V "$(_TCL_H)" TCL_VERSION >> version.vc] -!endif -!endif -!include version.vc -TCL_VERSION = $(TCL_DOTVERSION:.=) - -!if $(TCLINSTALL) -TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe" -!if !exist($(TCLSH)) && $(TCL_THREADS) -TCLSH = "$(_TCLDIR)\bin\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 -TCL_INCLUDES = -I"$(_TCLDIR)\include" -!else -TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe" -!if !exist($(TCLSH)) && $(TCL_THREADS) -TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\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 -TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win" -!endif - -!endif - -#---------------------------------------------------------- -# Optionally check for Tk info for building extensions. -#---------------------------------------------------------- - -!ifdef PROJECT_REQUIRES_TK -!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "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 - -!if defined(TKDIR) -TK_DOTVERSION = 8.4 -!if exist("$(_TK_H)") -!if [echo TK_DOTVERSION = \>> version.vc] \ - && [nmakehlp -V "$(_TK_H)" TK_VERSION >> version.vc] -!endif -!endif -!include version.vc -TK_VERSION = $(TK_DOTVERSION:.=) - -!if $(TKINSTALL) -WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe" -!if !exist($(WISH)) && $(TCL_THREADS) -WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)t$(SUFX).exe" -!endif -TKSTUBLIB = "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib" -TKIMPLIB = "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib" -TK_INCLUDES = -I"$(_TKDIR)\include" -TK_LIBRARY = $(_TKDIR)\lib -!else -WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe" -!if !exist($(WISH)) && $(TCL_THREADS) -WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\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_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib" -TK_LIBRARY = $(_TKDIR)\library -!endif - -!endif -!endif -!endif - - -#---------------------------------------------------------- -# Setup the fully qualified OUT_DIR path as OUT_DIR_PATH -#---------------------------------------------------------- -!if [echo OUT_DIR_PATH = \>> version.vc] \ - && [nmakehlp -Q "$(OUT_DIR)" >> version.vc] -!endif -!include version.vc +!if "$(MACHINE)" == "AMD64" +OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT +!endif +!if $(VCVERSION) < 1300 +OPTDEFINES = $(OPTDEFINES) -DNO_STRTOI64 +!endif + +# _ATL_XP_TARGETING - Newer SDK's need this to build for XP +COMPILERFLAGS = /D_ATL_XP_TARGETING + +# Following is primarily for the benefit of extensions. Tcl 8.5 builds +# Tcl without /DUNICODE, while 8.6 builds with it defined. When building +# an extension, it is advisable (but not mandated) to use the same Windows +# API as the Tcl build. This is accordingly defaulted below. A particular +# extension can override this by pre-definining USE_WIDECHAR_API. +!ifndef USE_WIDECHAR_API +!if $(TCL_VERSION) > 85 +USE_WIDECHAR_API = 1 +!else +USE_WIDECHAR_API = 0 +!endif +!endif + +!if $(USE_WIDECHAR_API) +COMPILERFLAGS = $(COMPILERFLAGS) /DUNICODE /D_UNICODE +!endif + +# Like the TEA system only set this non empty for non-Tk extensions +# Note: some extensions use PACKAGE_NAME and others use PACKAGE_TCLNAME +# so we pass both +!if !$(DOING_TCL) && !$(DOING_TK) +PKGNAMEFLAGS = -DPACKAGE_NAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \ + -DPACKAGE_TCLNAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \ + -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \ + -DMODULE_SCOPE=extern +!endif + +# crt picks the C run time based on selected OPTS +!if $(MSVCRT) +!if $(DEBUG) && !$(UNCHECKED) +crt = -MDd +!else +crt = -MD +!endif +!else +!if $(DEBUG) && !$(UNCHECKED) +crt = -MTd +!else +crt = -MT +!endif +!endif + +# cdebug includes compiler options for debugging as well as optimization. +!if $(DEBUG) + +# In debugging mode, optimizations need to be disabled +cdebug = -Zi -Od $(DEBUGFLAGS) + +!else + +cdebug = $(OPTIMIZATIONS) +!if $(SYMBOLS) +cdebug = $(cdebug) -Zi +!endif + +!endif # $(DEBUG) + +# cwarn includes default warning levels. +cwarn = $(WARNINGS) + +!if "$(MACHINE)" == "AMD64" +# Disable pointer<->int warnings related to cast between different sizes +# There are a gadzillion of these due to use of ClientData and +# clutter up compiler +# output increasing chance of a real warning getting lost. So disable them. +# Eventually some day, Tcl will be 64-bit clean. +cwarn = $(cwarn) -wd4311 -wd4312 +!endif + +### Common compiler options that are architecture specific +!if "$(MACHINE)" == "ARM" +carch = -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE +!else +carch = +!endif + +!if $(DEBUG) +# Turn warnings into errors +cwarn = $(cwarn) -WX +!endif + +INCLUDES = $(TCL_INCLUDES) $(TK_INCLUDES) $(PRJ_INCLUDES) +!if !$(DOING_TCL) && !$(DOING_TK) +INCLUDES = $(INCLUDES) -I"$(GENERICDIR)" -I"$(WINDIR)" -I"$(COMPATDIR)" +!endif + +# These flags are defined roughly in the order of the pre-reform +# rules.vc/makefile.vc to help visually compare that the pre- and +# post-reform build logs + +# cflags contains generic flags used for building practically all object files +cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\ $(cdebug) + +# appcflags contains $(cflags) and flags for building the application +# object files (e.g. tclsh, or wish) pkgcflags contains $(cflags) plus +# flags used for building shared object files The two differ in the +# BUILD_$(PROJECT) macro which should be defined only for the shared +# library *implementation* and not for its caller interface + +appcflags = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES) $(USE_STUBS_DEFS) +appcflags_nostubs = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES) +pkgcflags = $(appcflags) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT) +pkgcflags_nostubs = $(appcflags_nostubs) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT) + +# stubscflags contains $(cflags) plus flags used for building a stubs +# library for the package. Note: -DSTATIC_BUILD is defined in +# $(OPTDEFINES) only if the OPTS configuration indicates a static +# library. However the stubs library is ALWAYS static hence included +# here irrespective of the OPTS setting. +# +# TBD - tclvfs has a comment that stubs libs should not be compiled with -GL +# without stating why. Tcl itself compiled stubs libs with this flag. +# so we do not remove it from cflags. -GL may prevent extensions +# compiled with one VC version to fail to link against stubs library +# compiled with another VC version. Check for this and fix accordingly. +stubscflags = $(cflags) $(PKGNAMEFLAGS) $(PRJ_DEFINES) $(OPTDEFINES) -Zl -DSTATIC_BUILD $(INCLUDES) + +# Link flags + +!if $(DEBUG) +ldebug = -debug -debugtype:cv +!else +ldebug = -release -opt:ref -opt:icf,3 +!if $(SYMBOLS) +ldebug = $(ldebug) -debug -debugtype:cv +!endif +!endif + +# Note: Profiling is currently only possible with the Visual Studio Enterprise +!if $(PROFILE) +ldebug= $(ldebug) -profile +!endif + +### Declarations common to all linker versions +lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug) + +!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900 +lflags = $(lflags) -nodefaultlib:libucrt.lib +!endif + +# Old linkers (Visual C++ 6 in particular) will link for fast loading +# on Win98. Since we do not support Win98 any more, we specify nowin98 +# as recommended for NT and later. However, this is only required by +# IX86 on older compilers and only needed if we are not doing a static build. + +!if "$(MACHINE)" == "IX86" && !$(STATIC_BUILD) +!if [nmakehlp -l -opt:nowin98 $(LINKER_TESTFLAGS)] +# Align sections for PE size savings. +lflags = $(lflags) -opt:nowin98 +!endif +!endif + +dlllflags = $(lflags) -dll +conlflags = $(lflags) -subsystem:console +guilflags = $(lflags) -subsystem:windows + +# Libraries that are required for every image. +# Extensions should define any additional libraries with $(PRJ_LIBS) +winlibs = kernel32.lib advapi32.lib + +!if $(NEED_TK) +winlibs = $(winlibs) gdi32.lib user32.lib uxtheme.lib +!endif + +# Avoid 'unresolved external symbol __security_cookie' errors. +# c.f. http://support.microsoft.com/?id=894573 +!if "$(MACHINE)" == "AMD64" +!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500 +winlibs = $(winlibs) bufferoverflowU.lib +!endif +!endif + +baselibs = $(winlibs) $(PRJ_LIBS) + +!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900 +baselibs = $(baselibs) ucrt.lib +!endif + +################################################################ +# 13. Define standard commands, common make targets and implicit rules + +CCPKGCMD = $(cc32) $(pkgcflags) -Fo$(TMP_DIR)^\ +CCAPPCMD = $(cc32) $(appcflags) -Fo$(TMP_DIR)^\ +CCSTUBSCMD = $(cc32) $(stubscflags) -Fo$(TMP_DIR)^\ + +LIBCMD = $(lib32) -nologo $(LINKERFLAGS) -out:$@ +DLLCMD = $(link32) $(dlllflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs) + +CONEXECMD = $(link32) $(conlflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs) +GUIEXECMD = $(link32) $(guilflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs) +RESCMD = $(rc32) -fo $@ -r -i "$(GENERICDIR)" -i "$(TMP_DIR)" \ + $(TCL_INCLUDES) \ + -DDEBUG=$(DEBUG) -d UNCHECKED=$(UNCHECKED) \ + -DCOMMAVERSION=$(DOTVERSION:.=,),0 \ + -DDOTVERSION=\"$(DOTVERSION)\" \ + -DVERSION=\"$(VERSION)\" \ + -DSUFX=\"$(SUFX)\" \ + -DPROJECT=\"$(PROJECT)\" \ + -DPRJLIBNAME=\"$(PRJLIBNAME)\" + +!ifndef DEFAULT_BUILD_TARGET +DEFAULT_BUILD_TARGET = $(PROJECT) +!endif + +default-target: $(DEFAULT_BUILD_TARGET) + +default-pkgindex: + @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \ + [list load [file join $$dir $(PRJLIBNAME)]] > $(OUT_DIR)\pkgIndex.tcl + +default-pkgindex-tea: + @if exist $(ROOT)\pkgIndex.tcl.in nmakehlp -s << $(ROOT)\pkgIndex.tcl.in > $(OUT_DIR)\pkgIndex.tcl +@PACKAGE_VERSION@ $(DOTVERSION) +@PACKAGE_NAME@ $(PRJ_PACKAGE_TCLNAME) +@PACKAGE_TCLNAME@ $(PRJ_PACKAGE_TCLNAME) +@PKG_LIB_FILE@ $(PRJLIBNAME) +<< + + +default-install: default-install-binaries default-install-libraries + +default-install-binaries: $(PRJLIB) + @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)' + @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)" + @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL + +default-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) + +default-install-stubs: + @echo Installing stubs library to '$(SCRIPT_INSTALL_DIR)' + @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)" + @$(CPY) $(PRJSTUBLIB) "$(SCRIPT_INSTALL_DIR)" >NUL + +default-install-docs-html: + @echo Installing documentation files to '$(DOC_INSTALL_DIR)' + @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)" + @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.html" "$(DOCDIR)\*.css" "$(DOCDIR)\*.png") do @$(COPY) %f "$(DOC_INSTALL_DIR)" + +default-install-docs-n: + @echo Installing documentation files to '$(DOC_INSTALL_DIR)' + @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)" + @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.n") do @$(COPY) %f "$(DOC_INSTALL_DIR)" + +default-install-demos: + @echo Installing demos to '$(DEMO_INSTALL_DIR)' + @if not exist "$(DEMO_INSTALL_DIR)" mkdir "$(DEMO_INSTALL_DIR)" + @if exist $(DEMODIR) $(CPYDIR) "$(DEMODIR)" "$(DEMO_INSTALL_DIR)" + +default-clean: + @echo Cleaning $(TMP_DIR)\* ... + @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR) + @echo Cleaning $(WINDIR)\nmakehlp.obj, nmakehlp.exe ... + @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj + @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe + @if exist $(WINDIR)\nmakehlp.out del $(WINDIR)\nmakehlp.out + @echo Cleaning $(WINDIR)\nmhlp-out.txt ... + @if exist $(WINDIR)\nmhlp-out.txt del $(WINDIR)\nmhlp-out.txt + @echo Cleaning $(WINDIR)\_junk.pch ... + @if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch + @echo Cleaning $(WINDIR)\vercl.x, vercl.i ... + @if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x + @if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i + @echo Cleaning $(WINDIR)\versions.vc, version.vc ... + @if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc + @if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc + +default-hose: default-clean + @echo Hosing $(OUT_DIR)\* ... + @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR) + +# Only for backward compatibility +default-distclean: default-hose + +default-setup: + @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR) + @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR) + +!if "$(TESTPAT)" != "" +TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT) +!endif + +default-test: default-setup $(PROJECT) + @set TCLLIBPATH=$(OUT_DIR:\=/) + @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)" + cd "$(TESTDIR)" && $(DEBUGGER) $(TCLSH) all.tcl $(TESTFLAGS) + +default-shell: default-setup $(PROJECT) + @set TCLLIBPATH=$(OUT_DIR:\=/) + @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)" + $(DEBUGGER) $(TCLSH) + +# Generation of Windows version resource +!ifdef RCFILE + +# Note: don't use $** in below rule because there may be other dependencies +# and only the "master" rc must be passed to the resource compiler +$(TMP_DIR)\$(PROJECT).res: $(RCDIR)\$(PROJECT).rc + $(RESCMD) $(RCDIR)\$(PROJECT).rc + +!else + +# If parent makefile has not defined a resource definition file, +# we will generate one from standard template. +$(TMP_DIR)\$(PROJECT).res: $(TMP_DIR)\$(PROJECT).rc + +$(TMP_DIR)\$(PROJECT).rc: + @$(COPY) << $(TMP_DIR)\$(PROJECT).rc +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION COMMAVERSION + PRODUCTVERSION COMMAVERSION + FILEFLAGSMASK 0x3fL +#ifdef DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Tcl extension " PROJECT + VALUE "OriginalFilename", PRJLIBNAME + VALUE "FileVersion", DOTVERSION + VALUE "ProductName", "Package " PROJECT " for Tcl" + VALUE "ProductVersion", DOTVERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +<< + +!endif # ifdef RCFILE + +!ifndef DISABLE_IMPLICIT_RULES +DISABLE_IMPLICIT_RULES = 0 +!endif + +!if !$(DISABLE_IMPLICIT_RULES) +# Implicit rule definitions - only for building library objects. For stubs and +# main application, the master makefile should define explicit rules. + +{$(ROOT)}.c{$(TMP_DIR)}.obj:: + $(CCPKGCMD) @<< +$< +<< + +{$(WINDIR)}.c{$(TMP_DIR)}.obj:: + $(CCPKGCMD) @<< +$< +<< + +{$(GENERICDIR)}.c{$(TMP_DIR)}.obj:: + $(CCPKGCMD) @<< +$< +<< + +{$(COMPATDIR)}.c{$(TMP_DIR)}.obj:: + $(CCPKGCMD) @<< +$< +<< + +{$(RCDIR)}.rc{$(TMP_DIR)}.res: + $(RESCMD) $< + +{$(WINDIR)}.rc{$(TMP_DIR)}.res: + $(RESCMD) $< + +{$(TMP_DIR)}.rc{$(TMP_DIR)}.res: + $(RESCMD) $< + +.SUFFIXES: +.SUFFIXES:.c .rc + +!endif + +################################################################ +# 14. Sanity check selected options against Tcl build options +# When building an extension, certain configuration options should +# match the ones used when Tcl was built. Here we check and +# warn on a mismatch. +!if ! $(DOING_TCL) + +!if $(TCLINSTALL) # Building against an installed Tcl +!if exist("$(_TCLDIR)\lib\nmake\tcl.nmake") +TCLNMAKECONFIG = "$(_TCLDIR)\lib\nmake\tcl.nmake" +!endif +!else # ! $(TCLINSTALL) - building against Tcl source +!if exist("$(OUT_DIR)\tcl.nmake") +TCLNMAKECONFIG = "$(OUT_DIR)\tcl.nmake" +!endif +!endif # TCLINSTALL + +!if $(CONFIG_CHECK) +!ifdef TCLNMAKECONFIG +!include $(TCLNMAKECONFIG) + +!if defined(CORE_MACHINE) && "$(CORE_MACHINE)" != "$(MACHINE)" +!error ERROR: Build target ($(MACHINE)) does not match the Tcl library architecture ($(CORE_MACHINE)). +!endif +!if defined(CORE_USE_THREAD_ALLOC) && $(CORE_USE_THREAD_ALLOC) != $(USE_THREAD_ALLOC) +!message WARNING: Value of USE_THREAD_ALLOC ($(USE_THREAD_ALLOC)) does not match its Tcl core value ($(CORE_USE_THREAD_ALLOC)). +!endif +!if defined(CORE_DEBUG) && $(CORE_DEBUG) != $(DEBUG) +!message WARNING: Value of DEBUG ($(DEBUG)) does not match its Tcl library configuration ($(DEBUG)). +!endif +!endif + +!endif # TCLNMAKECONFIG + +!endif # ! $(DOING_TCL) #---------------------------------------------------------- # Display stats being used. #---------------------------------------------------------- +!if !$(DOING_TCL) +!message *** Building against Tcl at '$(_TCLDIR)' +!endif +!if !$(DOING_TK) && $(NEED_TK) +!message *** Building against Tk at '$(_TKDIR)' +!endif !message *** Intermediate directory will be '$(TMP_DIR)' !message *** Output directory will be '$(OUT_DIR)' +!message *** Installation, if selected, will be in '$(_INSTALLDIR)' !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)' +!message *** Compiler version $(VCVER). Target $(MACHINE), host $(NATIVE_ARCH). -!endif +!endif # ifdef _RULES_VC ADDED undroid/DiffUtilTcl/win/targets.vc Index: undroid/DiffUtilTcl/win/targets.vc ================================================================== --- /dev/null +++ undroid/DiffUtilTcl/win/targets.vc @@ -0,0 +1,98 @@ +#------------------------------------------------------------- -*- makefile -*- +# targets.vc -- +# +# Part of the nmake based build system for Tcl and its extensions. +# This file defines some standard targets for the convenience of extensions +# and can be optionally included by the extension makefile. +# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for docs. + +$(PROJECT): setup pkgindex $(PRJLIB) + +!ifdef PRJ_STUBOBJS +$(PROJECT): $(PRJSTUBLIB) +$(PRJSTUBLIB): $(PRJ_STUBOBJS) + $(LIBCMD) $** + +$(PRJ_STUBOBJS): + $(CCSTUBSCMD) %s +!endif # PRJ_STUBOBJS + +!ifdef PRJ_MANIFEST +$(PROJECT): $(PRJLIB).manifest +$(PRJLIB).manifest: $(PRJ_MANIFEST) + @nmakehlp -s << $** >$@ +@MACHINE@ $(MACHINE:IX86=X86) +<< +!endif + +!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk" +$(PRJLIB): $(PRJ_OBJS) $(RESFILE) +!if $(STATIC_BUILD) + $(LIBCMD) $** +!else + $(DLLCMD) $** + $(_VC_MANIFEST_EMBED_DLL) +!endif + -@del $*.exp +!endif + +!if "$(PRJ_HEADERS)" != "" && "$(PRJ_OBJS)" != "" +$(PRJ_OBJS): $(PRJ_HEADERS) +!endif + +# If parent makefile has defined stub objects, add their installation +# to the default install +!if "$(PRJ_STUBOBJS)" != "" +default-install: default-install-stubs +!endif + +# Unlike the other default targets, these cannot be in rules.vc because +# the executed command depends on existence of macro PRJ_HEADERS_PUBLIC +# that the parent makefile will not define until after including rules-ext.vc +!if "$(PRJ_HEADERS_PUBLIC)" != "" +default-install: default-install-headers +default-install-headers: + @echo Installing headers to '$(INCLUDE_INSTALL_DIR)' + @for %f in ($(PRJ_HEADERS_PUBLIC)) do @$(COPY) %f "$(INCLUDE_INSTALL_DIR)" +!endif + +!if "$(DISABLE_STANDARD_TARGETS)" == "" +DISABLE_STANDARD_TARGETS = 0 +!endif + +!if "$(DISABLE_TARGET_setup)" == "" +DISABLE_TARGET_setup = 0 +!endif +!if "$(DISABLE_TARGET_install)" == "" +DISABLE_TARGET_install = 0 +!endif +!if "$(DISABLE_TARGET_clean)" == "" +DISABLE_TARGET_clean = 0 +!endif +!if "$(DISABLE_TARGET_test)" == "" +DISABLE_TARGET_test = 0 +!endif +!if "$(DISABLE_TARGET_shell)" == "" +DISABLE_TARGET_shell = 0 +!endif + +!if !$(DISABLE_STANDARD_TARGETS) +!if !$(DISABLE_TARGET_setup) +setup: default-setup +!endif +!if !$(DISABLE_TARGET_install) +install: default-install +!endif +!if !$(DISABLE_TARGET_clean) +clean: default-clean +realclean: hose +hose: default-hose +distclean: realclean default-distclean +!endif +!if !$(DISABLE_TARGET_test) +test: default-test +!endif +!if !$(DISABLE_TARGET_shell) +shell: default-shell +!endif +!endif # DISABLE_STANDARD_TARGETS Index: undroid/tclcan/pkgIndex.tcl.in ================================================================== --- undroid/tclcan/pkgIndex.tcl.in +++ undroid/tclcan/pkgIndex.tcl.in @@ -1,6 +1,6 @@ # -# Tcl package index file, need proc can to catch the directory +# Tcl package index file, need a lambda to catch the directory # for run-time linking optional shared library libsocketcan.so # -proc can dir { load [file join $dir @PKG_LIB_FILE@] @PACKAGE_NAME@ } -package ifneeded @PACKAGE_NAME@ @PACKAGE_VERSION@ [list can $dir] +package ifneeded @PACKAGE_NAME@ @PACKAGE_VERSION@ [apply {dir { + load [file join $dir @PKG_LIB_FILE@] @PACKAGE_NAME@}} $dir] Index: undroid/tsb/examples/cheatsheet.tsb ================================================================== --- undroid/tsb/examples/cheatsheet.tsb +++ undroid/tsb/examples/cheatsheet.tsb @@ -1,71 +1,82 @@ -1 {h3 "Taygete Scrap Book Cheat Sheet"} 2 {h4 "Important (internal) global variables"} 3 {table 1 [info global {[A-Z]*}]} 4 {#HTML +1 {h3 "Taygete Scrap Book Cheat Sheet"} 2 {h4 "Important (internal) global variables"} 3 {table {{global variable}} [info global {[A-Z]*}]} 4 {#HTML
ID
The current input field during evaluation, an integer number.
W
The Webview for displaying the page.
H
The history array of input fields, keys are integer numbers starting from 1.
} 5 {# current state of history array -table 2 [array get H]} 6 {h4 "Useful procs in the global namespace"} 7 {info proc *} 8 {#HTML +table {key value} [array get H]} 6 {h4 "Useful procs in the global namespace"} 7 {lsort [info proc {[a-z]*}]} 8 {#HTML
-
parray arrayname ?pattern?
-
Pretty print an array using puts and thus outputs - after the corresponding input field.

-
htmlraw html ?hidden?
-
Output raw HTML after the corresponding input field, if hidden - is true, auto-hide the input field.

gets, read
Overriden, reading from stdin channel yields empty string.

-
puts
-
Overriden, writing to stdout/stderr channels - outputs after the corresponding input field.

h1 string, h2..h5
Format a HTML header after the correspondig input field, auto-hide the input field.

hr
Format a horizontal ruler, auto-hide the input field.

+
htmlraw html ?hidden?
+
Output raw HTML after the corresponding input field, if hidden + is true, auto-hide the input field.

img ?filename import mime?
Format a HTML IMG given filename, if file name omitted, present file selection. If import is true, the image is - inlined. If the mime type of the image is unknown it can be specified - with the mime parameter.

+ inlined. If the mime type of the image is unknown, it can be specified + with the mime parameter. The corresponding input field + is auto-hidden.
img_from_binary data mime ?hidden?
Format a HTML IMG given the byte array data and mime type mime. If hidden is false, the corresponding input field is not auto-hidden.

-
table ncols data
-
Format a HTML table, ncols gives the number of columns, - if negative, use the first -ncols items as header columns.
-
} 9 {h4 "Useful procs in the tsb namespace"} 10 {info proc tsb::*} 11 {#HTML +
parray arrayname ?pattern?
+
Pretty print an array using puts and thus outputs + after the corresponding input field.

+
puts
+
Overriden, writing to stdout/stderr channels + outputs after the corresponding input field.

+
table colinfo data
+
Format a HTML table, colinfo gives the number of columns, if it's + an integer; if negative, the first -colinfo items are used in the + header row. If colinfo is a list, it specifies the column headers + directly.
+
} 9 {h4 "Useful procs in the tsb namespace"} 10 {lsort [info proc tsb::*]} 11 {#HTML
-
tsb::load ?filename?
-
Load page from given filename, if file name omitted, present file selection.

-
tsb::save ?filename?
-
Save page to given filename, if file name omitted, present file selection.

tsb::canvas ?-width w -height h?
-
Creates a canvas emulation implementing enough methods for Plotchart, returns a widget command.

+
Creates a canvas emulation implementing enough methods + for Plotchart, returns a widget command.

tsb::canvascmd cmd ...
-
Implementation of the canvas widget command, supports create, delete and other methods plus method svg which renders the canvas as SVG after the corresponding input field.

+
Implementation of the canvas widget command, supports + create, delete and other methods plus + method svg which renders the canvas as SVG after + the corresponding input field.

tsb::clear
Clears the entire page.

tsb::eval id
Re-evaluates field with number id.

+
tsb::load ?filename?
+
Load page from given filename, if file name omitted, + present file selection.

tsb::print
-
Opens the print dialog to print the page.
+
Opens the print dialog to print the page. Platform dependent, + on Windows use <Control-p> from keyboard instead.

+
tsb::save ?filename?
+
Save page to given filename, if file name omitted, + present file selection.
} 12 {h4 "Input Fields"} 13 {#HTML

Input fields have a label of the form in(<number>) where the number is the index in the history array. Arbitraty text can be entered which however should be valid Tcl code, except the very first line is #HTML or #MARKDOWN, in which case the following lines should be valid HTML or Markdown text, respectively. The input field is evaluated when -<Shift>-<Return> is pressed.

+<Shift-Return> is pressed.

Depending on the command evaluation the input field is sometimes hidden. A double click on the corresponding output field(s) makes the input field visible and thus editable, again.

} 14 {h4 "Output Fields"} 15 {#HTML

Each input field has two corresponding output fields which are visible/non-empty depending on context. One output field receives preformatted text, either the result or the error message of a command evaluation. The other output field receives HTML, -e.g. for displaying an image or for the #HTML form from the input field.

} 16 {tsb::save cheatsheet.tsb} +e.g. for displaying an image or for the #HTML and #MARKDOWN +formats from the input field.

} 16 {tsb::save cheatsheet.tsb} Index: undroid/tsb/tsb.tcl ================================================================== --- undroid/tsb/tsb.tcl +++ undroid/tsb/tsb.tcl @@ -119,43 +119,50 @@ } } return } -# Quote angle brackets and double quotes for HTML output. +# Quote angle brackets et.al. for HTML output. proc ::tsb::htmlquote {str} { - regsub -all -- "&" $str "\\&" str - regsub -all -- "<" $str "\\<" str - regsub -all -- ">" $str "\\>" str - regsub -all -- "\"" $str "\\"" str - return $str + return [string map {& \& < \< > \> "\"" \"} $str] } -# Simple HTML table formatter. If number of columns is negative, -# make a header, otherwise all columns are uniform. +# Simple HTML table formatter. If "ncols" is negative, +# make a header, otherwise omit headers. If "ncols" is +# not an integer it's taken as header list. proc table {ncols data} { + if {![string is integer $ncols]} { + set tdata $data + set data $ncols + set ncols [expr {0 - [llength $data]}] + } if {$ncols == 0} { return } set ret "" if {$ncols < 0} { set ncols [expr {0 - $ncols}] append ret "" for {set i 0} {$i < $ncols} {incr i} { - append ret "" } append ret "" set data [lrange $data $ncols end] + } + if {[info exists tdata]} { + set data $tdata } while {[llength $data]} { append ret "" for {set i 0} {$i < $ncols} {incr i} { - append ret "" } append ret "" set data [lrange $data $ncols end] } @@ -208,13 +215,13 @@ set mime [::tsb::htmlquote $mime] } else { set mime image/[string tolower [file extension $name]] } if {![catch {open $name rb} f]} { - set ret "\n

" + append ret "'>\n" close $f if {$selname || $import} { set cmd [dict get [info frame -1] cmd] if {$import} { set newcmd "#HTML\n$ret" @@ -231,13 +238,13 @@ # Insert image from byte array. proc img_from_binary {data mime {hidden 1}} { set mime [::tsb::htmlquote $mime] - set ret "\n

" + append ret "'>\n" htmlraw $ret $hidden return } # Load and save functions. @@ -342,11 +349,11 @@ proc ::tsb::reload {args} { after cancel ::tsb::reload0 after idle ::tsb::reload0 } - + # Clear page. proc ::tsb::clear {} { variable inload if {$inload} { @@ -370,11 +377,11 @@ proc ::tsb::eval {id} { tailcall $::W call Feval $id } # Print page; on Windows seems not to work, but pressing -# opens the printer dialog. +# opens the printer dialog at least. proc ::tsb::print {} { tailcall $::W call window.print() } @@ -700,30 +707,57 @@ # JS core functions assembled from various pieces into a big string. variable D - set D {} - + # HEAD + set D {} + append D {} + append D {} + + # STYLE, CSS + append D { + textarea { + font-size: 100%; + } } } else { - append doc { - + } + } + + append D { + textarea { + border: 1px solid #AAAAAA; + margin: 5px 1px 3px 0px; + outline: none; + padding: 3px 0px 3px 3px; + -webkit-transition: all 0.05s ease-in-out; + -ms-transition: all 0.05s ease-in-out; + } + textarea:focus { + border: 1px solid rgba(81, 203, 238, 1); + box-shadow: 0 0 6px rgba(81, 203, 238, 1); + margin: 5px 1px 3px 0px; + padding: 3px 0px 3px 3px; } } + append D {} + + # SCRIPT append D { } + # BODY (empty), END append D {} # The URL to be displayed. # Windows: data:text/html, # MacOS: data:text/html, @@ -967,18 +1017,18 @@ set W [::twv::new -width 800 -height 600 -title $::tsb::title \ -url $::tsb::U -resizable 1 -debug 0 \ -callback ::tsb::call_from_js] -# On Windows this seems needed. +# On Windows this seems the way to load, maybe timing? if {!$::tsb::ready} { ::tsb::reload0 } -# Enter webview event loop. +# Enter the webview event loop. $W run # Done. exit 0
" \
+	    append ret "
" \ + "
" \
 		[::tsb::htmlquote [lindex $data $i]] "
" \
+	    append ret "
" \ + "
" \
 		[::tsb::htmlquote [lindex $data $i]] "