summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-04-23 22:27:58 (GMT)
committerSteven Knight <knight@baldmt.com>2003-04-23 22:27:58 (GMT)
commit9587e1d2dad1c532d86f664f5cbd6266ebd77808 (patch)
tree71ab8dbc059c0d16de3f5088427e288716d9dd43
parent9c4ebd90350becd6ff9b1b4e4049546680c849b6 (diff)
downloadSCons-9587e1d2dad1c532d86f664f5cbd6266ebd77808.zip
SCons-9587e1d2dad1c532d86f664f5cbd6266ebd77808.tar.gz
SCons-9587e1d2dad1c532d86f664f5cbd6266ebd77808.tar.bz2
Add support for MIDL. (Greg Spencer)
-rw-r--r--doc/man/scons.146
-rw-r--r--src/CHANGES.txt11
-rw-r--r--src/engine/MANIFEST.in2
-rw-r--r--src/engine/SCons/Platform/win32.py51
-rw-r--r--src/engine/SCons/Scanner/C.py103
-rw-r--r--src/engine/SCons/Scanner/Fortran.py72
-rw-r--r--src/engine/SCons/Scanner/IDL.py43
-rw-r--r--src/engine/SCons/Scanner/IDLTests.py428
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py178
-rw-r--r--src/engine/SCons/Scanner/__init__.py108
-rw-r--r--src/engine/SCons/Tool/__init__.py2
-rw-r--r--src/engine/SCons/Tool/midl.py70
-rw-r--r--src/engine/SCons/Tool/mslink.py29
-rw-r--r--src/engine/SCons/Tool/msvc.py4
-rw-r--r--src/engine/SCons/Util.py33
-rw-r--r--src/engine/SCons/UtilTests.py12
-rw-r--r--test/midl.py410
17 files changed, 1388 insertions, 214 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 91afc37..ffed57a 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -881,6 +881,7 @@ latex
lex
linkloc
masm
+midl
mingw
mslib
mslink
@@ -1231,6 +1232,23 @@ must have been built for a shared library
builder).
.B scons
will raise an error if there is any mismatch.
+.IP
+On WIN32 systems, specifying "register=1" will cause the dll to be
+registered after it is built using REGSVR32. The command that is run
+("regsvr32" by default) is determined by $REGSVR construction
+variable, and the flags passed are determined by $REGSVRFLAGS. By
+default, $REGSVRFLAGS includes "/s", to prevent dialogs from popping
+up and requiring user attention when it is run. If you change
+$REGSVRFLAGS, be sure to include "/s". For example,
+
+.ES
+env.SharedLibrary(target = 'bar',
+ source = ['bar.cxx', 'foo.obj'],
+ register=1)
+.EE
+
+.IP
+will register "bar.dll" as a COM object when it is done linking it.
.IP Library
A synonym for the
@@ -1310,6 +1328,19 @@ Example:
env.Java(target = 'classes', source = 'src')
.EE
+.IP TypeLibrary
+Builds a Windows type library (.tlb) file from and input IDL file
+(.idl). In addition, it will build the associated inteface stub and
+proxy source files. It names them according to the base name of the .idl file.
+.IP
+For example,
+
+.ES
+env.TypeLibrary(source="foo.idl")
+.EE
+.IP
+Will create foo.tlb, foo.h, foo_i.c, foo_p.c, and foo_data.c.
+
.IP DVI
Builds a .dvi file from a .tex, .ltx or .latex input file.
The suffix .dvi
@@ -4453,6 +4484,16 @@ not backslashes.
This is sometimes necessary on Win32 systems
when a path references a file on other (POSIX) systems.
+.IP srcpath
+The directory and file name to the source file linked to this file
+through BuildDir. If this file isn't linked, it just returns the
+directory and filename unchanged.
+
+.IP srcdir
+The directory containing the source file linked to this file
+through BuildDir. If this file isn't linked, it just returns the
+directory part of the filename.
+
.LP
For example, the specified target will
expand as follows for the corresponding modifiers:
@@ -4465,6 +4506,11 @@ ${TARGET.file} => file.x
${TARGET.filebase} => file
${TARGET.suffix} => .x
${TARGET.abspath} => /top/dir/sub/dir/file.x
+
+BuildDir('sub/dir','src')
+$SOURCE => sub/dir/file.x
+${SOURCE.srcpath} => src/file.x
+${SOURCE.srcdir} => src
.EE
Lastly, a variable name
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index dba2c7c..0e22ea7 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -88,6 +88,17 @@ RELEASE 0.14 - XXX
- Support the C preprocessor #import statement.
+ - Allow the SharedLibrary() Builder on Win32 systems to be able to
+ register a newly-built dll using regsvr32.
+
+ - Add a Builder for Windows type library (.tlb) files from IDL files.
+
+ - Add an IDL scanner.
+
+ - Refactor the Fortran, C and IDL scanners to share common logic.
+
+ - Add .srcpath and .srcdir attributes to $TARGET and $SOURCE.
+
From Christoph Wiedemann:
- Integrate David Snopek's "Autoscons" code as the new SConf
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 251b48a..cb26d18 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -22,6 +22,7 @@ SCons/Platform/posix.py
SCons/Platform/win32.py
SCons/Scanner/__init__.py
SCons/Scanner/C.py
+SCons/Scanner/IDL.py
SCons/Scanner/Fortran.py
SCons/Scanner/Prog.py
SCons/SConf.py
@@ -54,6 +55,7 @@ SCons/Tool/latex.py
SCons/Tool/lex.py
SCons/Tool/linkloc.py
SCons/Tool/masm.py
+SCons/Tool/midl.py
SCons/Tool/mingw.py
SCons/Tool/mslib.py
SCons/Tool/mslink.py
diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py
index d97e61e..4f7b05b 100644
--- a/src/engine/SCons/Platform/win32.py
+++ b/src/engine/SCons/Platform/win32.py
@@ -34,7 +34,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
-import popen2
import string
import sys
import tempfile
@@ -126,7 +125,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr):
# What went wrong here ??
pass
return ret
-
+
def spawn(sh, escape, cmd, args, env):
if not sh:
sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
@@ -145,8 +144,54 @@ def spawn(sh, escape, cmd, args, env):
# the arg.
escape = lambda x: '"' + x + '"'
-def generate(env):
+# Get the windows system directory name
+def get_system_root():
+ # A resonable default if we can't read the registry
+ try:
+ val = os.environ['SYSTEMROOT']
+ except:
+ val = "C:/WINDOWS"
+ pass
+ # First see if we can look in the registry...
+ if SCons.Util.can_read_reg:
+ try:
+ # Look for Windows NT system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows NT\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ except SCons.Util.RegError:
+ try:
+ # Okay, try the Windows 9x system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ except:
+ pass
+ return val
+
+# Get the location of the program files directory
+def get_program_files_dir():
+ # Now see if we can look in the registry...
+ val = ''
+ if SCons.Util.can_read_reg:
+ try:
+ # Look for Windows Program Files directory
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir')
+ except SCons.Util.RegError:
+ val = ''
+ pass
+
+ if val == '':
+ # A reasonable default if we can't read the registry
+ # (Actually, it's pretty reasonable even if we can :-)
+ val = os.path.join(os.path.dirname(get_system_root()),"Program Files")
+
+ return val
+
+def generate(env):
# Attempt to find cmd.exe (for WinNT/2k/XP) or
# command.com for Win9x
cmd_interp = ''
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
index c06774d..44007f9 100644
--- a/src/engine/SCons/Scanner/C.py
+++ b/src/engine/SCons/Scanner/C.py
@@ -29,106 +29,17 @@ This module implements the depenency scanner for C/C++ code.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
-import re
-
-import SCons.Node
import SCons.Node.FS
import SCons.Scanner
-import SCons.Util
-import SCons.Warnings
-
-include_re = re.compile('^[ \t]*#[ \t]*(?:include|import)[ \t]+(<|")([^>"]+)(>|")', re.M)
def CScan(fs = SCons.Node.FS.default_fs):
"""Return a prototype Scanner instance for scanning source files
that use the C pre-processor"""
- cs = SCons.Scanner.Current(scan, "CScan", fs,
- [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
- ".h", ".H", ".hxx", ".hpp", ".hh",
- ".F", ".fpp", ".FPP"],
- path_function = path,
- recursive = 1)
+ cs = SCons.Scanner.ClassicCPP("CScan",
+ [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+ ".h", ".H", ".hxx", ".hpp", ".hh",
+ ".F", ".fpp", ".FPP"],
+ "CPPPATH",
+ '^[ \t]*#[ \t]*(?:include|import)[ \t]+(<|")([^>"]+)(>|")',
+ fs = fs)
return cs
-
-def path(env, dir, fs = SCons.Node.FS.default_fs):
- try:
- cpppath = env['CPPPATH']
- except KeyError:
- return ()
- return tuple(fs.Rsearchall(SCons.Util.mapPaths(cpppath, dir, env),
- clazz = SCons.Node.FS.Dir,
- must_exist = 0))
-
-def scan(node, env, cpppath = (), fs = SCons.Node.FS.default_fs):
- """
- scan(node, Environment) -> [node]
-
- the C/C++ dependency scanner function
-
- This function is intentionally simple. There are two rules it
- follows:
-
- 1) #include <foo.h> - search for foo.h in CPPPATH followed by the
- directory 'filename' is in
- 2) #include \"foo.h\" - search for foo.h in the directory 'filename' is
- in followed by CPPPATH
-
- These rules approximate the behaviour of most C/C++ compilers.
-
- This scanner also ignores #ifdef and other preprocessor conditionals, so
- it may find more depencies than there really are, but it never misses
- dependencies.
- """
-
- node = node.rfile()
-
- # This function caches the following information:
- # node.includes - the result of include_re.findall()
-
- if not node.exists():
- return []
-
- # cache the includes list in node so we only scan it once:
- if node.includes != None:
- includes = node.includes
- else:
- includes = include_re.findall(node.get_contents())
- node.includes = includes
-
- nodes = []
- source_dir = node.get_dir()
- for include in includes:
- if include[0] == '"':
- n = SCons.Node.FS.find_file(include[1],
- (source_dir,) + cpppath,
- fs.File)
- else:
- n = SCons.Node.FS.find_file(include[1],
- cpppath + (source_dir,),
- fs.File)
-
- if not n is None:
- nodes.append(n)
- else:
- SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
- "No dependency generated for file: %s (included from: %s) -- file not found" % (include[1], node))
-
- # Schwartzian transform from the Python FAQ Wizard
- def st(List, Metric):
- def pairing(element, M = Metric):
- return (M(element), element)
- def stripit(pair):
- return pair[1]
- paired = map(pairing, List)
- paired.sort()
- return map(stripit, paired)
-
- def normalize(node):
- # We don't want the order of includes to be
- # modified by case changes on case insensitive OSes, so
- # normalize the case of the filename here:
- # (see test/win32pathmadness.py for a test of this)
- return SCons.Node.FS._my_normcase(str(node))
-
- return st(nodes, normalize)
diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py
index 10f0836..2e44eb9 100644
--- a/src/engine/SCons/Scanner/Fortran.py
+++ b/src/engine/SCons/Scanner/Fortran.py
@@ -38,76 +38,12 @@ import SCons.Scanner
import SCons.Util
import SCons.Warnings
-include_re = re.compile("INCLUDE[ \t]+'([\\w./\\\\]+)'", re.M)
-
def FortranScan(fs = SCons.Node.FS.default_fs):
"""Return a prototype Scanner instance for scanning source files
for Fortran INCLUDE statements"""
- scanner = SCons.Scanner.Current(scan, "FortranScan", fs,
+ scanner = SCons.Scanner.Classic("FortranScan",
[".f", ".F", ".for", ".FOR"],
- path_function = path,
- recursive = 1)
+ "F77PATH",
+ "INCLUDE[ \t]+'([\\w./\\\\]+)'",
+ fs = fs)
return scanner
-
-def path(env, dir, fs = SCons.Node.FS.default_fs):
- try:
- f77path = env['F77PATH']
- except KeyError:
- return ()
- return tuple(fs.Rsearchall(SCons.Util.mapPaths(f77path, dir, env),
- clazz = SCons.Node.FS.Dir,
- must_exist = 0))
-
-def scan(node, env, f77path = (), fs = SCons.Node.FS.default_fs):
- """
- scan(node, Environment) -> [node]
-
- the Fortran dependency scanner function
- """
-
- node = node.rfile()
-
- # This function caches the following information:
- # node.includes - the result of include_re.findall()
-
- if not node.exists():
- return []
-
- # cache the includes list in node so we only scan it once:
- if node.includes != None:
- includes = node.includes
- else:
- includes = include_re.findall(node.get_contents())
- node.includes = includes
-
- source_dir = node.get_dir()
-
- nodes = []
- for include in includes:
- n = SCons.Node.FS.find_file(include,
- (source_dir,) + f77path,
- fs.File)
- if not n is None:
- nodes.append(n)
- else:
- SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
- "No dependency generated for file: %s (included from: %s) -- file not found" % (include, node))
-
- # Schwartzian transform from the Python FAQ Wizard
- def st(List, Metric):
- def pairing(element, M = Metric):
- return (M(element), element)
- def stripit(pair):
- return pair[1]
- paired = map(pairing, List)
- paired.sort()
- return map(stripit, paired)
-
- def normalize(node):
- # We don't want the order of includes to be
- # modified by case changes on case insensitive OSes, so
- # normalize the case of the filename here:
- # (see test/win32pathmadness.py for a test of this)
- return SCons.Node.FS._my_normcase(str(node))
-
- return st(nodes, normalize)
diff --git a/src/engine/SCons/Scanner/IDL.py b/src/engine/SCons/Scanner/IDL.py
new file mode 100644
index 0000000..e4ceac8
--- /dev/null
+++ b/src/engine/SCons/Scanner/IDL.py
@@ -0,0 +1,43 @@
+"""SCons.Scanner.IDL
+
+This module implements the depenency scanner for IDL (Interface
+Definition Language) files.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/IDL.py 0.D013 2003/03/31 21:46:41 software"
+
+import SCons.Node.FS
+import SCons.Scanner
+
+def IDLScan(fs = SCons.Node.FS.default_fs):
+ """Return a prototype Scanner instance for scanning IDL source files"""
+ cs = SCons.Scanner.ClassicCPP("IDLScan",
+ [".idl", ".IDL"],
+ "CPPPATH",
+ '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")',
+ fs = fs)
+ return cs
diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py
new file mode 100644
index 0000000..09e8427
--- /dev/null
+++ b/src/engine/SCons/Scanner/IDLTests.py
@@ -0,0 +1,428 @@
+#
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/IDLTests.py 0.D013 2003/03/31 21:46:41 software"
+
+import TestCmd
+import SCons.Scanner.IDL
+import unittest
+import sys
+import os
+import os.path
+import SCons.Node.FS
+import SCons.Warnings
+
+test = TestCmd.TestCmd(workdir = '')
+
+os.chdir(test.workpath(''))
+
+# create some source files and headers:
+
+test.write('t1.idl','''
+#include "f1.idl"
+#include <f2.idl>
+import "f3.idl";
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring("IBarObject Interface"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+''')
+
+test.write('t2.idl',"""
+#include \"d1/f1.idl\"
+#include <d2/f1.idl>
+#include \"f1.idl\"
+import <f3.idl>;
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring(\"IBarObject Interface\"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+""")
+
+test.write('t3.idl',"""
+#include \t \"f1.idl\"
+ \t #include \"f2.idl\"
+# \t include \"f3-test.idl\"
+
+#include \t <d1/f1.idl>
+ \t #include <d1/f2.idl>
+# \t include <d1/f3-test.idl>
+
+import \t \"d1/f1.idl\"
+ \t import \"d1/f2.idl\"
+
+include \t \"never.idl\"
+ \t include \"never.idl\"
+
+// #include \"never.idl\"
+
+const char* x = \"#include <never.idl>\"
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring(\"IBarObject Interface\"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+""")
+
+test.subdir('d1', ['d1', 'd2'])
+
+headers = ['f1.idl','f2.idl', 'f3.idl', 'f3-test.idl', 'fi.idl', 'fj.idl', 'never.idl',
+ 'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl', 'd1/fi.idl', 'd1/fj.idl',
+ 'd1/d2/f1.idl', 'd1/d2/f2.idl', 'd1/d2/f3-test.idl',
+ 'd1/d2/f4.idl', 'd1/d2/fi.idl', 'd1/d2/fj.idl']
+
+for h in headers:
+ test.write(h, " ")
+
+test.write('f2.idl',"""
+#include "fi.idl"
+""")
+
+test.write('f3-test.idl',"""
+#include <fj.idl>
+""")
+
+
+test.subdir('include', 'subdir', ['subdir', 'include'])
+
+test.write('t4.idl',"""
+#include \"fa.idl\"
+#include <fb.idl>
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring(\"IBarObject Interface\"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+""")
+
+test.write(['include', 'fa.idl'], "\n")
+test.write(['include', 'fb.idl'], "\n")
+test.write(['subdir', 'include', 'fa.idl'], "\n")
+test.write(['subdir', 'include', 'fb.idl'], "\n")
+
+test.subdir('repository', ['repository', 'include'],
+ ['repository', 'src' ])
+test.subdir('work', ['work', 'src'])
+
+test.write(['repository', 'include', 'iii.idl'], "\n")
+
+test.write(['work', 'src', 'fff.c'], """
+#include <iii.idl>
+#include <jjj.idl>
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'work', 'src', 'aaa.c'], """
+#include "bbb.idl"
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'work', 'src', 'bbb.idl'], "\n")
+
+test.write([ 'repository', 'src', 'ccc.c'], """
+#include "ddd.idl"
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'repository', 'src', 'ddd.idl'], "\n")
+
+# define some helpers:
+
+class DummyEnvironment:
+ def __init__(self, listCppPath):
+ self.path = listCppPath
+
+ def Dictionary(self, *args):
+ if not args:
+ return { 'CPPPATH': self.path }
+ elif len(args) == 1 and args[0] == 'CPPPATH':
+ return self.path
+ else:
+ raise KeyError, "Dummy environment only has CPPPATH attribute."
+
+ def subst(self, arg):
+ return arg
+
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
+ def __getitem__(self,key):
+ return self.Dictionary()[key]
+
+ def __setitem__(self,key,value):
+ self.Dictionary()[key] = value
+
+ def __delitem__(self,key):
+ del self.Dictionary()[key]
+
+global my_normpath
+my_normpath = os.path.normpath
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+
+def deps_match(self, deps, headers):
+ scanned = map(my_normpath, map(str, deps))
+ expect = map(my_normpath, headers)
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+def make_node(filename, fs=SCons.Node.FS.default_fs):
+ return fs.File(test.workpath(filename))
+
+# define some tests:
+
+class IDLScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(make_node('t1.idl'), env, path)
+ headers = ['f1.idl', 'f2.idl', 'f3.idl']
+ deps_match(self, deps, map(test.workpath, headers))
+
+class IDLScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(make_node('t1.idl'), env, path)
+ headers = ['d1/f2.idl', 'f1.idl', 'f3.idl']
+ deps_match(self, deps, map(test.workpath, headers))
+
+class IDLScannerTestCase3(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(make_node('t2.idl'), env, path)
+ headers = ['d1/d2/f1.idl', 'd1/f1.idl', 'f1.idl', 'f3.idl']
+ deps_match(self, deps, map(test.workpath, headers))
+
+class IDLScannerTestCase4(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(make_node('t2.idl'), env, path)
+ headers = ['d1/d2/f1.idl', 'd1/f1.idl', 'f1.idl', 'f3.idl']
+ deps_match(self, deps, map(test.workpath, headers))
+
+class IDLScannerTestCase5(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+
+ n = make_node('t3.idl')
+ def my_rexists(s=n):
+ s.rexists_called = 1
+ return s.old_rexists()
+ setattr(n, 'old_rexists', n.rexists)
+ setattr(n, 'rexists', my_rexists)
+
+ deps = s(n, env, path)
+
+ # Make sure rexists() got called on the file node being
+ # scanned, essential for cooperation with BuildDir functionality.
+ assert n.rexists_called
+
+ headers = ['d1/f1.idl', 'd1/f1.idl', 'd1/f2.idl', 'd1/f2.idl', 'd1/f3-test.idl',
+ 'f1.idl', 'f2.idl', 'f3-test.idl']
+ deps_match(self, deps, map(test.workpath, headers))
+
+class IDLScannerTestCase6(unittest.TestCase):
+ def runTest(self):
+ env1 = DummyEnvironment([test.workpath("d1")])
+ env2 = DummyEnvironment([test.workpath("d1/d2")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path1 = s.path(env1)
+ path2 = s.path(env2)
+ deps1 = s(make_node('t1.idl'), env1, path1)
+ deps2 = s(make_node('t1.idl'), env2, path2)
+ headers1 = ['d1/f2.idl', 'f1.idl', 'f3.idl']
+ headers2 = ['d1/d2/f2.idl', 'f1.idl', 'f3.idl']
+ deps_match(self, deps1, map(test.workpath, headers1))
+ deps_match(self, deps2, map(test.workpath, headers2))
+
+class IDLScannerTestCase7(unittest.TestCase):
+ def runTest(self):
+ fs = SCons.Node.FS.FS(test.workpath(''))
+ env = DummyEnvironment(["include"])
+ s = SCons.Scanner.IDL.IDLScan(fs = fs)
+ path = s.path(env)
+ deps1 = s(fs.File('t4.idl'), env, path)
+ fs.chdir(fs.Dir('subdir'))
+ dir = fs.getcwd()
+ fs.chdir(fs.Dir('..'))
+ path = s.path(env, dir)
+ deps2 = s(fs.File('#t4.idl'), env, path)
+ headers1 = ['include/fa.idl', 'include/fb.idl']
+ headers2 = ['subdir/include/fa.idl', 'subdir/include/fb.idl']
+ deps_match(self, deps1, headers1)
+ deps_match(self, deps2, headers2)
+
+class IDLScannerTestCase8(unittest.TestCase):
+ def runTest(self):
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
+ class TestOut:
+ def __call__(self, x):
+ self.out = x
+
+ to = TestOut()
+ to.out = None
+ SCons.Warnings._warningOut = to
+ test.write('fa.idl','\n')
+ fs = SCons.Node.FS.FS(test.workpath(''))
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan(fs=fs)
+ path = s.path(env)
+ deps = s(fs.File('t4.idl'), env, path)
+
+ # Did we catch the warning associated with not finding fb.idl?
+ assert to.out
+
+ deps_match(self, deps, [ 'fa.idl' ])
+ test.unlink('fa.idl')
+
+class IDLScannerTestCase9(unittest.TestCase):
+ def runTest(self):
+ fs = SCons.Node.FS.FS(test.workpath(''))
+ fs.chdir(fs.Dir('include'))
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan(fs=fs)
+ path = s.path(env)
+ test.write('include/t4.idl', test.read('t4.idl'))
+ deps = s(fs.File('#include/t4.idl'), env, path)
+ fs.chdir(fs.Dir('..'))
+ deps_match(self, deps, [ 'include/fa.idl', 'include/fb.idl' ])
+ test.unlink('include/t4.idl')
+
+class IDLScannerTestCase10(unittest.TestCase):
+ def runTest(self):
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.Repository(test.workpath('repository'))
+
+ # Create a derived file in a directory that does not exist yet.
+ # This was a bug at one time.
+ f1=fs.File('include2/jjj.idl')
+ f1.builder=1
+ env = DummyEnvironment(['include', 'include2'])
+ s = SCons.Scanner.IDL.IDLScan(fs=fs)
+ path = s.path(env)
+ deps = s(fs.File('src/fff.c'), env, path)
+ deps_match(self, deps, [ test.workpath('repository/include/iii.idl'), 'include2/jjj.idl' ])
+ os.chdir(test.workpath(''))
+
+class IDLScannerTestCase11(unittest.TestCase):
+ def runTest(self):
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.BuildDir('build1', 'src', 1)
+ fs.BuildDir('build2', 'src', 0)
+ fs.Repository(test.workpath('repository'))
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan(fs = fs)
+ path = s.path(env)
+ deps1 = s(fs.File('build1/aaa.c'), env, path)
+ deps_match(self, deps1, [ 'build1/bbb.idl' ])
+ deps2 = s(fs.File('build2/aaa.c'), env, path)
+ deps_match(self, deps2, [ 'src/bbb.idl' ])
+ deps3 = s(fs.File('build1/ccc.c'), env, path)
+ deps_match(self, deps3, [ 'build1/ddd.idl' ])
+ deps4 = s(fs.File('build2/ccc.c'), env, path)
+ deps_match(self, deps4, [ test.workpath('repository/src/ddd.idl') ])
+ os.chdir(test.workpath(''))
+
+class IDLScannerTestCase12(unittest.TestCase):
+ def runTest(self):
+ class SubstEnvironment(DummyEnvironment):
+ def subst(self, arg, test=test):
+ return test.workpath("d1")
+ env = SubstEnvironment(["blah"])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(make_node('t1.idl'), env, path)
+ headers = ['d1/f2.idl', 'f1.idl', 'f3.idl']
+ deps_match(self, deps, map(test.workpath, headers))
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(IDLScannerTestCase1())
+ suite.addTest(IDLScannerTestCase2())
+ suite.addTest(IDLScannerTestCase3())
+ suite.addTest(IDLScannerTestCase4())
+ suite.addTest(IDLScannerTestCase5())
+ suite.addTest(IDLScannerTestCase6())
+ suite.addTest(IDLScannerTestCase7())
+ suite.addTest(IDLScannerTestCase8())
+ suite.addTest(IDLScannerTestCase9())
+ suite.addTest(IDLScannerTestCase10())
+ suite.addTest(IDLScannerTestCase11())
+ suite.addTest(IDLScannerTestCase12())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
index d9882a6..8c8744d 100644
--- a/src/engine/SCons/Scanner/ScannerTests.py
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -27,7 +27,10 @@ import unittest
import SCons.Scanner
import sys
-class ScannerTestBase:
+class DummyEnvironment:
+ pass
+
+class ScannerTestCase(unittest.TestCase):
def func(self, filename, env, target, *args):
self.filename = filename
@@ -39,7 +42,6 @@ class ScannerTestBase:
return self.deps
-
def test(self, scanner, env, filename, deps, *args):
self.deps = deps
path = scanner.path(env)
@@ -57,13 +59,8 @@ class ScannerTestBase:
else:
self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
-class DummyEnvironment:
- pass
-
-
-class ScannerPositionalTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Base class using the position argument"
- def runTest(self):
+ def test_positional(self):
+ """Test the Scanner.Base class using positional arguments"""
s = SCons.Scanner.Base(self.func, "Pos")
env = DummyEnvironment()
env.VARIABLE = "var1"
@@ -73,9 +70,8 @@ class ScannerPositionalTestCase(ScannerTestBase, unittest.TestCase):
env.VARIABLE = "i1"
self.test(s, env, 'i1.cpp', ['i1.h', 'i1.hpp'])
-class ScannerKeywordTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Base class using the keyword argument"
- def runTest(self):
+ def test_keywords(self):
+ """Test the Scanner.Base class using keyword arguments"""
s = SCons.Scanner.Base(function = self.func, name = "Key")
env = DummyEnvironment()
env.VARIABLE = "var2"
@@ -85,9 +81,8 @@ class ScannerKeywordTestCase(ScannerTestBase, unittest.TestCase):
env.VARIABLE = "i2"
self.test(s, env, 'i2.cpp', ['i2.h', 'i2.hpp'])
-class ScannerPositionalArgumentTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Base class using both position and optional arguments"
- def runTest(self):
+ def test_pos_opt(self):
+ """Test the Scanner.Base class using both position and optional arguments"""
arg = "this is the argument"
s = SCons.Scanner.Base(self.func, "PosArg", arg)
env = DummyEnvironment()
@@ -98,9 +93,8 @@ class ScannerPositionalArgumentTestCase(ScannerTestBase, unittest.TestCase):
env.VARIABLE = "i3"
self.test(s, env, 'i3.cpp', ['i3.h', 'i3.hpp'], arg)
-class ScannerKeywordArgumentTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Base class using both keyword and optional arguments"
- def runTest(self):
+ def test_key_opt(self):
+ """Test the Scanner.Base class using both keyword and optional arguments"""
arg = "this is another argument"
s = SCons.Scanner.Base(function = self.func, name = "KeyArg",
argument = arg)
@@ -112,20 +106,16 @@ class ScannerKeywordArgumentTestCase(ScannerTestBase, unittest.TestCase):
env.VARIABLE = "i4"
self.test(s, env, 'i4.cpp', ['i4.h', 'i4.hpp'], arg)
-class ScannerHashTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Base class __hash__() method"
- def runTest(self):
+ def test_hash(self):
+ """Test the Scanner.Base class __hash__() method"""
s = SCons.Scanner.Base(self.func, "Hash")
dict = {}
dict[s] = 777
self.failUnless(hash(dict.keys()[0]) == hash(repr(s)),
"did not hash Scanner base class as expected")
-class ScannerCheckTestCase(unittest.TestCase):
- "Test the Scanner.Base class scan_check method"
- def setUp(self):
- self.checked = {}
- def runTest(self):
+ def test_scan_check(self):
+ """Test the Scanner.Base class scan_check() method"""
def my_scan(filename, env, target, *args):
return []
def check(node, s=self):
@@ -133,14 +123,14 @@ class ScannerCheckTestCase(unittest.TestCase):
return 1
env = DummyEnvironment()
s = SCons.Scanner.Base(my_scan, "Check", scan_check = check)
+ self.checked = {}
path = s.path(env)
scanned = s('x', env, path)
self.failUnless(self.checked['x'] == 1,
"did not call check function")
-class ScannerRecursiveTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Base class recursive flag"
- def runTest(self):
+ def test_recursive(self):
+ """Test the Scanner.Base class recursive flag"""
s = SCons.Scanner.Base(function = self.func)
self.failUnless(s.recursive == None,
"incorrect default recursive value")
@@ -151,9 +141,9 @@ class ScannerRecursiveTestCase(ScannerTestBase, unittest.TestCase):
self.failUnless(s.recursive == 1,
"did not set recursive flag to 1")
-class CurrentTestCase(ScannerTestBase, unittest.TestCase):
- "Test the Scanner.Current class"
- def runTest(self):
+class CurrentTestCase(unittest.TestCase):
+ def test_class(self):
+ """Test the Scanner.Current class"""
class MyNode:
def __init__(self):
self.called_has_builder = None
@@ -199,16 +189,122 @@ class CurrentTestCase(ScannerTestBase, unittest.TestCase):
self.failUnless(ic.called_current, "did not call current()")
self.failUnless(ic.func_called, "did not call func()")
+class ClassicTestCase(unittest.TestCase):
+ def test_find_include(self):
+ """Test the Scanner.Classic find_include() method"""
+ env = DummyEnvironment()
+ s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
+
+ def _find_file(filename, paths, factory):
+ return paths[0]+'/'+filename
+
+ save = SCons.Node.FS.find_file
+ SCons.Node.FS.find_file = _find_file
+
+ try:
+ n, i = s.find_include('aaa', 'foo', ('path',))
+ assert n == 'foo/aaa', n
+ assert i == 'aaa', i
+
+ finally:
+ SCons.Node.FS.find_file = save
+
+ def test_scan(self):
+ """Test the Scanner.Classic scan() method"""
+ class MyNode:
+ def __init__(self, name):
+ self.name = name
+ self._rfile = self
+ self.includes = None
+ def rfile(self):
+ return self._rfile
+ def exists(self):
+ return self._exists
+ def get_contents(self):
+ return self._contents
+ def get_dir(self):
+ return self._dir
+
+ class MyScanner(SCons.Scanner.Classic):
+ def find_include(self, include, source_dir, path):
+ return include, include
+
+ env = DummyEnvironment()
+ s = MyScanner("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
+
+ # If the node doesn't exist, scanning turns up nothing.
+ n1 = MyNode("n1")
+ n1._exists = None
+ ret = s.scan(n1, env)
+ assert ret == [], ret
+
+ # Verify that it finds includes from the contents.
+ n = MyNode("n")
+ n._exists = 1
+ n._dir = MyNode("n._dir")
+ n._contents = 'my_inc abc\n'
+ ret = s.scan(n, env)
+ assert ret == ['abc'], ret
+
+ # Verify that it uses the cached include info.
+ n._contents = 'my_inc def\n'
+ ret = s.scan(n, env)
+ assert ret == ['abc'], ret
+
+ # Verify that if we wipe the cache, it uses the new contents.
+ n.includes = None
+ ret = s.scan(n, env)
+ assert ret == ['def'], ret
+
+ # Verify that it sorts what it finds.
+ n.includes = ['xyz', 'uvw']
+ ret = s.scan(n, env)
+ assert ret == ['uvw', 'xyz'], ret
+
+ # Verify that we use the rfile() node.
+ nr = MyNode("nr")
+ nr._exists = 1
+ nr._dir = MyNode("nr._dir")
+ nr.includes = ['jkl', 'mno']
+ n._rfile = nr
+ ret = s.scan(n, env)
+ assert ret == ['jkl', 'mno'], ret
+
+class ClassicCPPTestCase(unittest.TestCase):
+ def test_find_include(self):
+ """Test the Scanner.ClassicCPP find_include() method"""
+ env = DummyEnvironment()
+ s = SCons.Scanner.ClassicCPP("Test", [], None, "")
+
+ def _find_file(filename, paths, factory):
+ return paths[0]+'/'+filename
+
+ save = SCons.Node.FS.find_file
+ SCons.Node.FS.find_file = _find_file
+
+ try:
+ n, i = s.find_include(('"', 'aaa'), 'foo', ('path',))
+ assert n == 'foo/aaa', n
+ assert i == 'aaa', i
+
+ n, i = s.find_include(('<', 'bbb'), 'foo', ('path',))
+ assert n == 'path/bbb', n
+ assert i == 'bbb', i
+
+ finally:
+ SCons.Node.FS.find_file = _find_file
+
def suite():
suite = unittest.TestSuite()
- suite.addTest(ScannerPositionalTestCase())
- suite.addTest(ScannerKeywordTestCase())
- suite.addTest(ScannerPositionalArgumentTestCase())
- suite.addTest(ScannerKeywordArgumentTestCase())
- suite.addTest(ScannerHashTestCase())
- suite.addTest(ScannerCheckTestCase())
- suite.addTest(ScannerRecursiveTestCase())
- suite.addTest(CurrentTestCase())
+ tclasses = [
+ ScannerTestCase,
+ CurrentTestCase,
+ ClassicTestCase,
+ ClassicCPPTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
return suite
if __name__ == "__main__":
@@ -216,5 +312,3 @@ if __name__ == "__main__":
result = runner.run(suite())
if not result.wasSuccessful():
sys.exit(1)
-
-
diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py
index d4ee523..2146ebe 100644
--- a/src/engine/SCons/Scanner/__init__.py
+++ b/src/engine/SCons/Scanner/__init__.py
@@ -29,6 +29,7 @@ The Scanner package for the SCons software construction utility.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import re
import SCons.Node.FS
import SCons.Sig
@@ -180,3 +181,110 @@ class Current(Base):
return c
kw['scan_check'] = current_check
apply(Base.__init__, (self,) + args, kw)
+
+class Classic(Current):
+ """
+ A Scanner subclass to contain the common logic for classic CPP-style
+ include scanning, but which can be customized to use different
+ regular expressions to find the includes.
+
+ Note that in order for this to work "out of the box" (without
+ overriding the find_include() method), the regular expression passed
+ to the constructor must return the name of the include file in group
+ 0.
+ """
+
+ def __init__(self, name, suffixes, path_variable, regex,
+ fs=SCons.Node.FS.default_fs, *args, **kw):
+
+ self.cre = re.compile(regex, re.M)
+ self.fs = fs
+
+ def _path(env, dir, pv=path_variable, fs=fs):
+ try:
+ path = env[pv]
+ except KeyError:
+ return ()
+ return tuple(fs.Rsearchall(SCons.Util.mapPaths(path, dir, env),
+ clazz = SCons.Node.FS.Dir,
+ must_exist = 0))
+
+ def _scan(node, env, path, self=self, fs=fs):
+ return self.scan(node, env, path)
+
+ kw['function'] = _scan
+ kw['path_function'] = _path
+ kw['recursive'] = 1
+ kw['skeys'] = suffixes
+
+ apply(Current.__init__, (self,) + args, kw)
+
+ def find_include(self, include, source_dir, path):
+ n = SCons.Node.FS.find_file(include, (source_dir,) + path, self.fs.File)
+ return n, include
+
+ def scan(self, node, env, path=()):
+ node = node.rfile()
+
+ if not node.exists():
+ return []
+
+ # cache the includes list in node so we only scan it once:
+ if node.includes != None:
+ includes = node.includes
+ else:
+ includes = self.cre.findall(node.get_contents())
+ node.includes = includes
+
+ nodes = []
+ source_dir = node.get_dir()
+ for include in includes:
+ n, i = self.find_include(include, source_dir, path)
+
+ if not n is None:
+ nodes.append(n)
+ else:
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
+
+ # Schwartzian transform from the Python FAQ Wizard
+ def st(List, Metric):
+ def pairing(element, M = Metric):
+ return (M(element), element)
+ def stripit(pair):
+ return pair[1]
+ paired = map(pairing, List)
+ paired.sort()
+ return map(stripit, paired)
+
+ def normalize(node):
+ # We don't want the order of includes to be
+ # modified by case changes on case insensitive OSes, so
+ # normalize the case of the filename here:
+ # (see test/win32pathmadness.py for a test of this)
+ return SCons.Node.FS._my_normcase(str(node))
+
+ transformed = st(nodes, normalize)
+ # print "Classic: " + str(node) + " => " + str(map(lambda x: str(x),list(transformed)))
+ return transformed
+
+class ClassicCPP(Classic):
+ """
+ A Classic Scanner subclass which takes into account the type of
+ bracketing used to include the file, and uses classic CPP rules
+ for searching for the files based on the bracketing.
+
+ Note that in order for this to work, the regular expression passed
+ to the constructor must return the leading bracket in group 0, and
+ the contained filename in group 1.
+ """
+ def find_include(self, include, source_dir, path):
+ if include[0] == '"':
+ n = SCons.Node.FS.find_file(include[1],
+ (source_dir,) + path,
+ self.fs.File)
+ else:
+ n = SCons.Node.FS.find_file(include[1],
+ path + (source_dir,),
+ self.fs.File)
+ return n, include[1]
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index 27ef3e1..c1e4d01 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -203,7 +203,7 @@ def tool_list(platform, env):
other_tools = FindAllTools(['BitKeeper', 'CVS',
'dvipdf', 'dvips', 'gs',
'jar', 'javac',
- 'latex', 'lex',
+ 'latex', 'lex', 'midl',
'pdflatex', 'pdftex', 'Perforce',
'RCS', 'SCCS',
# 'Subversion',
diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py
new file mode 100644
index 0000000..e78f76b
--- /dev/null
+++ b/src/engine/SCons/Tool/midl.py
@@ -0,0 +1,70 @@
+"""SCons.Tool.midl
+
+Tool-specific initialization for midl (Microsoft IDL compiler).
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/midl.py 0.D013 2003/03/31 21:46:41 software"
+
+import SCons.Defaults
+import SCons.Scanner.IDL
+import os.path
+
+def midl_emitter(target, source, env):
+ """Produces a list of outputs from the MIDL compiler"""
+ base, ext = os.path.splitext(source[0])
+ tlb = base + '.tlb'
+ incl = base + '.h'
+ interface = base + '_i.c'
+ proxy = base + '_p.c'
+ dlldata = base + '_data.c'
+
+ t = [tlb, incl, interface, proxy, dlldata]
+
+ return (t,source)
+
+idl_scanner = SCons.Scanner.IDL.IDLScan()
+
+midl_builder = SCons.Builder.Builder(action='$MIDLCOM',
+ src_suffix = '.idl',
+ suffix='.tlb',
+ emitter = midl_emitter,
+ scanner = idl_scanner)
+
+def generate(env):
+ """Add Builders and construction variables for midl to an Environment."""
+
+ env['MIDL'] = 'MIDL.EXE'
+ env['MIDLFLAGS'] = '/nologo'
+ env['MIDLCOM'] = "$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL"
+ env['BUILDERS']['TypeLibrary'] = midl_builder
+
+def exists(env):
+ return env.Detect('midl')
diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py
index 5522b0a..4bf8f0e 100644
--- a/src/engine/SCons/Tool/mslink.py
+++ b/src/engine/SCons/Tool/mslink.py
@@ -106,6 +106,10 @@ def win32LibEmitter(target, source, env):
target.append(env.ReplaceIxes(dll,
"SHLIBPREFIX", "SHLIBSUFFIX",
"LIBPREFIX", "LIBSUFFIX"))
+ # and .exp file is created if there are exports from a DLL
+ target.append(env.ReplaceIxes(dll,
+ "SHLIBPREFIX", "SHLIBSUFFIX",
+ "WIN32EXPPREFIX", "WIN32EXPSUFFIX"))
return (target, source)
@@ -118,6 +122,21 @@ def prog_emitter(target, source, env):
return (target,source)
+def RegServerFunc(target, source, env):
+ if env.has_key('register') and env['register']:
+ ret = regServerAction([target[0]], [source[0]], env)
+ if ret:
+ raise SCons.Errors.UserError, "Unable to register %s" % target[0]
+ else:
+ print "Registered %s sucessfully" % target[0]
+ return ret
+ return 0
+
+regServerAction = SCons.Action.Action("$REGSVRCOM")
+regServerCheck = SCons.Action.Action(RegServerFunc, None)
+shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}')
+compositeLinkAction = shlibLinkAction + regServerCheck
+
def generate(env):
"""Add Builders and construction variables for ar to an Environment."""
env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
@@ -127,7 +146,7 @@ def generate(env):
env['SHLINKFLAGS'] = '$LINKFLAGS /dll'
env['_SHLINK_TARGETS'] = win32ShlinkTargets
env['_SHLINK_SOURCES'] = win32ShlinkSources
- env['SHLINKCOM'] = '${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}'
+ env['SHLINKCOM'] = compositeLinkAction
env['SHLIBEMITTER']= win32LibEmitter
env['LINK'] = 'link'
env['LINKFLAGS'] = '/nologo'
@@ -143,6 +162,14 @@ def generate(env):
env['WIN32DEFSUFFIX'] = '.def'
env['WIN32_INSERT_DEF'] = 0
+ env['WIN32EXPPREFIX'] = ''
+ env['WIN32EXPSUFFIX'] = '.exp'
+
+ env['REGSVRACTION'] = regServerCheck
+ env['REGSVR'] = 'regsvr32'
+ env['REGSVRFLAGS'] = '/s '
+ env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS $TARGET'
+
if SCons.Util.can_read_reg:
include_path, lib_path, exe_path = get_msdev_paths()
env['ENV']['LIB'] = lib_path
diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py
index eaaec3e..6f0c516 100644
--- a/src/engine/SCons/Tool/msvc.py
+++ b/src/engine/SCons/Tool/msvc.py
@@ -41,6 +41,7 @@ import SCons.Tool
import SCons.Errors
import SCons.Builder
import SCons.Util
+import SCons.Platform.win32
CSuffixes = ['.c', '.C']
CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
@@ -172,7 +173,8 @@ def get_msdev_paths(version=None):
else:
# The DevStudio environment variables don't exist,
# so just use the variables from the source environment.
- MVSdir = r'C:\Program Files\Microsoft Visual Studio'
+ progfiles = SCons.Platform.win32.get_program_files_dir()
+ MVSdir = os.path.join(progfiles,r'Microsoft Visual Studio')
MVSVCdir = r'%s\VC98' % MVSdir
MVSCommondir = r'%s\Common' % MVSdir
try:
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 3a9d3b7..a570a71 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -40,6 +40,7 @@ import sys
import types
import UserDict
import UserList
+import SCons.Node
try:
from UserString import UserString
@@ -166,6 +167,31 @@ class PathList(UserList.UserList):
# available even if this object is a Lister, not a PathList.
return PathList(map(lambda x: updrive(os.path.abspath(x)), self.data))
+ def __getSrcDir(self):
+ """Return the directory containing the linked
+ source file, or this file path, if not linked"""
+ sp = self.__splitPath()[0]
+ rv = []
+ for dir in sp:
+ dn = SCons.Node.FS.default_fs.Dir(str(dir))
+ if (dn == None):
+ rv = rv + ['']
+ else:
+ rv = rv + [str(dn.srcnode())]
+ return PathList(rv)
+
+ def __getSrcPath(self):
+ """Return the path to the linked source file,
+ or this file path, if not linked"""
+ rv = []
+ for dir in self.data:
+ fn = SCons.Node.FS.default_fs.File(str(dir))
+ if (fn == None):
+ rv = rv + ['']
+ else:
+ rv = rv + [str(fn.srcnode())]
+ return PathList(rv)
+
def __posix(self):
if os.sep == '/':
return self
@@ -178,11 +204,14 @@ class PathList(UserList.UserList):
"dir" : __getDir,
"suffix" : __getSuffix,
"abspath" : __getAbsPath,
- "posix" : __posix}
+ "srcpath" : __getSrcPath,
+ "srcdir" : __getSrcDir,
+ "posix" : __posix
+ }
def is_literal(self):
return 1
-
+
def __str__(self):
return string.join(self.data)
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index e0788da..5f0eaa8 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -163,6 +163,18 @@ class UtilTestCase(unittest.TestCase):
target=target, source=source)
assert newcom == "test foo/bar.exe foo/blah.cpp", newcom
+ SCons.Node.FS.default_fs.BuildDir("#baz","#foo")
+
+ newcom = scons_subst("test ${SOURCE.srcdir}", env,
+ target=target, source=['baz/bar.c'])
+
+ assert newcom == cvt("test foo"), newcom
+
+ newcom = scons_subst("test ${SOURCE.srcpath}", env,
+ target=target, source=['baz/bar.c'])
+
+ assert newcom == cvt("test foo/bar.c"), newcom
+
newcom = scons_subst("test $xxx", env)
assert newcom == cvt("test"), newcom
diff --git a/test/midl.py b/test/midl.py
new file mode 100644
index 0000000..59fba57
--- /dev/null
+++ b/test/midl.py
@@ -0,0 +1,410 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "test/midl.py 0.D013 2003/03/31 21:46:41 software"
+
+import TestSCons
+import sys
+import os.path
+import os
+import TestCmd
+import time
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+if sys.platform != 'win32':
+ test.pass_test()
+
+#####
+# Test the basics
+
+test.write('SConstruct',"""
+import os.path
+import os
+
+build = '#build'
+env = Environment(CCFLAGS = ' -nologo ', CPPPATH='${TARGET.dir}')
+Export('env','build')
+
+BuildDir(build, 'src')
+SConscript(os.path.join(build,'SConscript'))
+""")
+
+test.subdir('src','build')
+
+test.write('src/SConscript',"""
+import os.path
+
+Import('env','build')
+
+local = env.Copy(WIN32_INSERT_DEF = 1)
+
+barsrc = [
+ 'BarObject.cpp',
+ 'bar.cpp',
+ local.RES('bar.rc', RCFLAGS= '/I\"${SOURCE.srcdir}\"'),
+ ]
+
+local.TypeLibrary('bar.idl')
+
+local.SharedLibrary(target = 'bar.dll',
+ source = barsrc,
+ PCH=local.PCH('BarPCH.cpp')[0],
+ PCHSTOP = 'BarPCH.h',
+ register=1)
+""")
+
+test.write('src/BarObject.cpp','''
+#include "BarPCH.h"
+#include "Bar.h"
+#include "BarObject.h"
+''')
+
+test.write('src/BarObject.h','''
+#ifndef __BAROBJECT_H_
+#define __BAROBJECT_H_
+
+#include "resource.h"
+
+class ATL_NO_VTABLE CBarObject :
+ public CComObjectRootEx<CComSingleThreadModel>,
+ public CComCoClass<CBarObject, &CLSID_BarObject>,
+ public IDispatchImpl<IBarObject, &IID_IBarObject, &LIBID_BARLib>
+{
+public:
+ CBarObject()
+ {
+ }
+
+DECLARE_REGISTRY_RESOURCEID(IDR_BAROBJECT)
+
+DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+BEGIN_COM_MAP(CBarObject)
+ COM_INTERFACE_ENTRY(IBarObject)
+ COM_INTERFACE_ENTRY(IDispatch)
+END_COM_MAP()
+
+public:
+};
+
+#endif
+''')
+
+test.write('src/BarObject.rgs',"""
+HKCR
+{
+ Bar.BarObject.1 = s 'BarObject Class'
+ {
+ CLSID = s '{640BE9EC-B79D-4C9E-BB64-95D24854A303}'
+ }
+ Bar.BarObject = s 'BarObject Class'
+ {
+ CLSID = s '{640BE9EC-B79D-4C9E-BB64-95D24854A303}'
+ CurVer = s 'Bar.BarObject.1'
+ }
+ NoRemove CLSID
+ {
+ ForceRemove {640BE9EC-B79D-4C9E-BB64-95D24854A303} = s 'BarObject Class'
+ {
+ ProgID = s 'Bar.BarObject.1'
+ VersionIndependentProgID = s 'Bar.BarObject'
+ ForceRemove 'Programmable'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Apartment'
+ }
+ 'TypeLib' = s '{73E5EA5F-9D45-463F-AA33-9F376AF7B643}'
+ }
+ }
+}
+""")
+
+test.write('src/BarPCH.cpp','''
+#include "BarPCH.h"
+
+#ifdef _ATL_STATIC_REGISTRY
+#include <statreg.h>
+#include <statreg.cpp>
+#endif
+
+#include <atlimpl.cpp>
+''')
+
+test.write('src/BarPCH.h','''
+#ifndef BarPCH_h
+#define BarPCH_h
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define STRICT
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif
+#define _ATL_APARTMENT_THREADED
+
+#include <atlbase.h>
+extern CComModule _Module;
+#include <atlcom.h>
+
+#endif
+''')
+
+test.write('src/bar.cpp','''
+#include "BarPCH.h"
+#include "resource.h"
+#include <initguid.h>
+#include "bar.h"
+
+#include "bar_i.c"
+#include "BarObject.h"
+
+CComModule _Module;
+
+BEGIN_OBJECT_MAP(ObjectMap)
+OBJECT_ENTRY(CLSID_BarObject, CBarObject)
+END_OBJECT_MAP()
+
+extern "C"
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+ lpReserved;
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ _Module.Init(ObjectMap, hInstance, &LIBID_BARLib);
+ DisableThreadLibraryCalls(hInstance);
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ _Module.Term();
+ return TRUE; // ok
+}
+
+STDAPI DllCanUnloadNow(void)
+{
+ return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
+{
+ return _Module.GetClassObject(rclsid, riid, ppv);
+}
+
+STDAPI DllRegisterServer(void)
+{
+ return _Module.RegisterServer(TRUE);
+}
+
+STDAPI DllUnregisterServer(void)
+{
+ return _Module.UnregisterServer(TRUE);
+}
+''')
+
+test.write('src/bar.def','''
+; bar.def : Declares the module parameters.
+
+LIBRARY "bar.DLL"
+
+EXPORTS
+ DllCanUnloadNow @1 PRIVATE
+ DllGetClassObject @2 PRIVATE
+ DllRegisterServer @3 PRIVATE
+ DllUnregisterServer @4 PRIVATE
+''')
+
+test.write('src/bar.idl','''
+import "oaidl.idl";
+import "ocidl.idl";
+ [
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring("IBarObject Interface"),
+ pointer_default(unique)
+ ]
+ interface IBarObject : IDispatch
+ {
+ };
+
+[
+ uuid(73E5EA5F-9D45-463F-AA33-9F376AF7B643),
+ version(1.0),
+ helpstring("bar 1.0 Type Library")
+]
+library BARLib
+{
+ importlib("stdole32.tlb");
+ importlib("stdole2.tlb");
+
+ [
+ uuid(640BE9EC-B79D-4C9E-BB64-95D24854A303),
+ helpstring("BarObject Class")
+ ]
+ coclass BarObject
+ {
+ [default] interface IBarObject;
+ };
+};
+''')
+
+test.write('src/bar.rc','''
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+
+#include "winres.h"
+
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""winres.h""\\r\\n"
+ "\\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "1 TYPELIB ""bar.tlb""\\r\\n"
+ "\\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "\\0"
+ VALUE "FileDescription", "bar Module\\0"
+ VALUE "FileVersion", "1, 0, 0, 1\\0"
+ VALUE "InternalName", "bar\\0"
+ VALUE "LegalCopyright", "Copyright 2003\\0"
+ VALUE "OriginalFilename", "bar.DLL\\0"
+ VALUE "ProductName", "bar Module\\0"
+ VALUE "ProductVersion", "1, 0, 0, 1\\0"
+ VALUE "OLESelfRegister", "\\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+IDR_BAROBJECT REGISTRY DISCARDABLE "BarObject.rgs"
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_PROJNAME "bar"
+END
+
+#endif // English (U.S.) resources
+
+#ifndef APSTUDIO_INVOKED
+
+1 TYPELIB "bar.tlb"
+
+#endif // not APSTUDIO_INVOKED
+''')
+
+test.write('src/resource.h','''
+#define IDS_PROJNAME 100
+#define IDR_BAROBJECT 101
+
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 201
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 201
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
+''')
+
+test.run(arguments=os.path.join('build','bar.dll'))
+
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','BarPCH.pch'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','BarPCH.obj'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.tlb'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.h'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar_i.c'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar_p.c'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar_data.c'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','BarObject.obj'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.obj'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.res'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.dll'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.lib'))))
+test.fail_test(not os.path.exists(test.workpath(os.path.join('build','bar.exp'))))
+
+test.run(arguments='-c .')
+
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','BarPCH.pch'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','BarPCH.obj'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.tlb'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.h'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar_i.c'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar_p.c'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar_data.c'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','BarObject.obj'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.obj'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.res'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.dll'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.lib'))))
+test.fail_test(os.path.exists(test.workpath(os.path.join('build','bar.exp'))))
+
+test.pass_test()