/[ascend]/trunk/scons/nsis.py
ViewVC logotype

Diff of /trunk/scons/nsis.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 578 by johnpye, Tue May 9 04:59:03 2006 UTC revision 1408 by jpye, Mon Apr 23 02:32:34 2007 UTC
# Line 1  Line 1 
 # NSIS Support for SCons  
 # Written by Mike Elkins, January 2004  
 # Provided 'as-is', it works for me!  
   
   
 """  
 This tool provides SCons support for the Nullsoft Scriptable Install System  
 a windows installer builder available at http://nsis.sourceforge.net/home  
   
   
 To use it you must copy this file into the scons/SCons/Tools directory or use  
 the tooldir arg in the Tool function and put a line like 'env.Tool("NSIS")'  
 into your file. Then you can do 'env.Installer("foobar")' which will read foobar.nsi and  
 create dependencies on all the files you put into your installer, so that if  
 anything changes your installer will be rebuilt.  It also makes the target  
 equal to the filename you specified in foobar.nsi.  Wildcards are handled correctly.  
   
 In addition, if you set NSISDEFINES to a dictionary, those variables will be passed  
 to NSIS.  
 """  
   
   
   
 import SCons.Builder  
 import SCons.Util  
 import SCons.Scanner  
 import SCons.Sig  
 import os.path  
 import glob  
   
   
 def nsis_parse( sources, keyword, multiple ):  
   """  
   A function that knows how to read a .nsi file and figure  
   out what files are referenced, or find the 'OutFile' line.  
   
   
   sources is a list of nsi files.  
   keyword is the command ('File' or 'OutFile') to look for  
   multiple is true if you want all the args as a list, false if you  
   just want the first one.  
   """  
   stuff = []  
   for s in sources:  
     c = s.get_contents()  
     for l in c.split('\n'):  
       semi = l.find(';')  
       if (semi != -1):  
         l = l[:semi]  
       hash = l.find('#')  
       if (hash != -1):  
         l = l[:hash]  
       # Look for the keyword  
       l = l.strip()  
       spl = l.split(None,1)  
       if len(spl) > 1:  
         if spl[0].capitalize() == keyword.capitalize():  
           arg = spl[1]  
           if arg.startswith('"') and arg.endswith('"'):  
             arg = arg[1:-1]  
           if multiple:  
             stuff += [ arg ]  
           else:  
             return arg  
   return stuff  
   
   
 def nsis_path( filename, nsisdefines, rootdir ):  
   """  
   Do environment replacement, and prepend with the SCons root dir if  
   necessary  
   """  
   # We can't do variables defined by NSIS itself (like $INSTDIR),  
   # only user supplied ones (like ${FOO})  
   varPos = filename.find('${')  
   while varPos != -1:  
     endpos = filename.find('}',varPos)  
     assert endpos != -1  
     if not nsisdefines.has_key(filename[varPos+2:endpos]):  
       raise KeyError ("Could not find %s in NSISDEFINES" % filename[varPos+2:endpos])  
     val = nsisdefines[filename[varPos+2:endpos]]  
     if type(val) == list:  
       if varPos != 0 or endpos+1 != len(filename):  
         raise Exception("Can't use lists on variables that aren't complete filenames")  
       return val  
     filename = filename[0:varPos] + val + filename[endpos+1:]  
     varPos = filename.find('${')  
   return filename  
   
   
 def nsis_scanner( node, env, path ):  
   """  
   The scanner that looks through the source .nsi files and finds all lines  
   that are the 'File' command, fixes the directories etc, and returns them.  
   """  
   nodes = node.rfile()  
   if not node.exists():  
     return []  
   nodes = []  
   source_dir = node.get_dir()  
   for include in nsis_parse([node],'file',1):  
     exp = nsis_path(include,env['NSISDEFINES'],source_dir)  
     if type(exp) != list:  
       exp = [exp]  
     for p in exp:  
       for filename in glob.glob( os.path.abspath(  
         os.path.join(str(source_dir),p))):  
           # Why absolute path?  Cause it breaks mysteriously without it :(  
           nodes.append(filename)  
   return nodes  
   
   
 def nsis_emitter( source, target, env ):  
   """  
   The emitter changes the target name to match what the command actually will  
   output, which is the argument to the OutFile command.  
   """  
   nsp = nsis_parse(source,'outfile',0)  
   if not nsp:  
     return (target,source)  
   x  = (  
     nsis_path(nsp,env['NSISDEFINES'],''),  
     source)  
   return x  
   
 def quoteIfSpaced(text):  
   if ' ' in text:  
     return '"'+text+'"'  
   else:  
     return text  
   
 def toString(item,env):  
   if type(item) == list:  
     ret = ''  
     for i in item:  
       if ret:  
         ret += ' '  
       val = toString(i,env)  
       if ' ' in val:  
         val = "'"+val+"'"  
       ret += val  
     return ret  
   else:  
     # For convienence, handle #s here  
     if str(item).startswith('#'):  
       item = env.File(item).get_abspath()  
     return str(item)  
   
 def runNSIS(source,target,env,for_signature):  
   ret = env['NSIS']+" "  
   if env.has_key('NSISFLAGS'):  
     for flag in env['NSISFLAGS']:  
       ret += flag  
       ret += ' '  
   if env.has_key('NSISDEFINES'):  
     for d in env['NSISDEFINES']:  
       ret += '/D'+d  
       if env['NSISDEFINES'][d]:  
         ret +='='+quoteIfSpaced(toString(env['NSISDEFINES'][d],env))  
       ret += ' '  
   for s in source:  
     ret += quoteIfSpaced(str(s))  
   return ret  
   
 def generate(env):  
   """  
   This function adds NSIS support to your environment.  
   """  
   env['BUILDERS']['Installer'] = SCons.Builder.Builder(generator=runNSIS,  
                                  src_suffix='.nsi',  
                                  emitter=nsis_emitter)  
   env.Append(SCANNERS = SCons.Scanner.Scanner( function = nsis_scanner,  
              skeys = ['.nsi']))  
   if not env.has_key('NSISDEFINES'):  
     env['NSISDEFINES'] = {}  
   env['NSIS'] = find_nsis(env)  
   
 def find_nsis(env):  
   """  
   Try and figure out if NSIS is installed on this machine, and if so,  
   where.  
   """  
   if SCons.Util.can_read_reg:  
     # If we can read the registry, get the NSIS command from it  
     try:  
       k = SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,  
                                   'SOFTWARE\\NSIS')  
       val, tok = SCons.Util.RegQueryValueEx(k,None)  
       ret = val + os.path.sep + 'makensis.exe'  
       if os.path.exists(ret):  
         return '"' + ret + '"'  
       else:  
         return None  
     except:  
       pass # Couldn't find the key, just act like we can't read the registry  
   # Hope it's on the path  
   return env.WhereIs('makensis.exe')  
   
 def exists(env):  
   """  
   Is NSIS findable on this machine?  
   """  
   if find_nsis(env) != None:  
     return 1  
   return 0  
1    # NSIS Support for SCons
2    # Written by Mike Elkins, January 2004
3    # Provided 'as-is', it works for me!
4    
5    
6    """
7    This tool provides SCons support for the Nullsoft Scriptable Install System
8    a windows installer builder available at http://nsis.sourceforge.net/home
9    
10    
11    To use it you must copy this file into the scons/SCons/Tools directory or use
12    the tooldir arg in the Tool function and put a line like 'env.Tool("NSIS")'
13    into your file. Then you can do 'env.Installer("foobar")' which will read foobar.nsi and
14    create dependencies on all the files you put into your installer, so that if
15    anything changes your installer will be rebuilt.  It also makes the target
16    equal to the filename you specified in foobar.nsi.  Wildcards are handled correctly.
17    
18    In addition, if you set NSISDEFINES to a dictionary, those variables will be passed
19    to NSIS.
20    """
21    
22    
23    
24    import SCons.Builder
25    import SCons.Util
26    import SCons.Scanner
27    import SCons.Sig
28    import os.path
29    import glob
30    
31    
32    def nsis_parse( sources, keyword, multiple ):
33      """
34      A function that knows how to read a .nsi file and figure
35      out what files are referenced, or find the 'OutFile' line.
36    
37    
38      sources is a list of nsi files.
39      keyword is the command ('File' or 'OutFile') to look for
40      multiple is true if you want all the args as a list, false if you
41      just want the first one.
42      """
43      stuff = []
44      for s in sources:
45        c = s.get_contents()
46        for l in c.split('\n'):
47          semi = l.find(';')
48          if (semi != -1):
49            l = l[:semi]
50          hash = l.find('#')
51          if (hash != -1):
52            l = l[:hash]
53          # Look for the keyword
54          l = l.strip()
55          spl = l.split(None,1)
56          if len(spl) > 1:
57            if spl[0].capitalize() == keyword.capitalize():
58              arg = spl[1]
59              if arg.startswith('"') and arg.endswith('"'):
60                arg = arg[1:-1]
61              if multiple:
62                stuff += [ arg ]
63              else:
64                return arg
65      return stuff
66    
67    
68    def nsis_path( filename, nsisdefines, rootdir ):
69      """
70      Do environment replacement, and prepend with the SCons root dir if
71      necessary
72      """
73      # We can't do variables defined by NSIS itself (like $INSTDIR),
74      # only user supplied ones (like ${FOO})
75      varPos = filename.find('${')
76      while varPos != -1:
77        endpos = filename.find('}',varPos)
78        assert endpos != -1
79        if not nsisdefines.has_key(filename[varPos+2:endpos]):
80          raise KeyError ("Could not find %s in NSISDEFINES" % filename[varPos+2:endpos])
81        val = nsisdefines[filename[varPos+2:endpos]]
82        if type(val) == list:
83          if varPos != 0 or endpos+1 != len(filename):
84            raise Exception("Can't use lists on variables that aren't complete filenames")
85          return val
86        filename = filename[0:varPos] + val + filename[endpos+1:]
87        varPos = filename.find('${')
88      return filename
89    
90    
91    def nsis_scanner( node, env, path ):
92      """
93      The scanner that looks through the source .nsi files and finds all lines
94      that are the 'File' command, fixes the directories etc, and returns them.
95      """
96      nodes = node.rfile()
97      if not node.exists():
98        return []
99      nodes = []
100      source_dir = node.get_dir()
101      for include in nsis_parse([node],'file',1):
102        exp = nsis_path(include,env['NSISDEFINES'],source_dir)
103        if type(exp) != list:
104          exp = [exp]
105        for p in exp:
106          for filename in glob.glob( os.path.abspath(
107            os.path.join(str(source_dir),p))):
108              # Why absolute path?  Cause it breaks mysteriously without it :(
109              nodes.append(filename)
110      return nodes
111    
112    
113    def nsis_emitter( source, target, env ):
114      """
115      The emitter changes the target name to match what the command actually will
116      output, which is the argument to the OutFile command.
117      """
118      nsp = nsis_parse(source,'outfile',0)
119      if not nsp:
120        return (target,source)
121      x  = (
122        nsis_path(nsp,env['NSISDEFINES'],''),
123        source)
124      return x
125    
126    def quoteIfSpaced(text):
127      if ' ' in text:
128        return '"'+text+'"'
129      else:
130        return text
131    
132    def toString(item,env):
133      if type(item) == list:
134        ret = ''
135        for i in item:
136          if ret:
137            ret += ' '
138          val = toString(i,env)
139          if ' ' in val:
140            val = "'"+val+"'"
141          ret += val
142        return ret
143      else:
144        # For convienence, handle #s here
145        if str(item).startswith('#'):
146          item = env.File(item).get_abspath()
147        return str(item)
148    
149    def runNSIS(source,target,env,for_signature):
150      ret = env['NSIS']+" "
151      if env.has_key('NSISFLAGS'):
152        for flag in env['NSISFLAGS']:
153          ret += flag
154          ret += ' '
155      if env.has_key('NSISDEFINES'):
156        for d in env['NSISDEFINES']:
157          ret += '/D'+d
158          if env['NSISDEFINES'][d]:
159            ret +='='+quoteIfSpaced(toString(env['NSISDEFINES'][d],env))
160          ret += ' '
161      for s in source:
162        ret += quoteIfSpaced(str(s))
163      return ret
164    
165    def generate(env):
166      """
167      This function adds NSIS support to your environment.
168      """
169      env['BUILDERS']['Installer'] = SCons.Builder.Builder(generator=runNSIS,
170                                     src_suffix='.nsi',
171                                     emitter=nsis_emitter)
172      env.Append(SCANNERS = SCons.Scanner.Scanner( function = nsis_scanner,
173                 skeys = ['.nsi']))
174      if not env.has_key('NSISDEFINES'):
175        env['NSISDEFINES'] = {}
176      env['NSIS'] = find_nsis(env)
177    
178    def find_nsis(env):
179      """
180      Try and figure out if NSIS is installed on this machine, and if so,
181      where.
182      """
183      if SCons.Util.can_read_reg:
184        # If we can read the registry, get the NSIS command from it
185        try:
186          k = SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
187                                      'SOFTWARE\\NSIS')
188          val, tok = SCons.Util.RegQueryValueEx(k,None)
189          ret = val + os.path.sep + 'makensis.exe'
190          if os.path.exists(ret):
191            return '"' + ret + '"'
192          else:
193            return None
194        except:
195          pass # Couldn't find the key, just act like we can't read the registry
196      # Hope it's on the path
197      return env.WhereIs('makensis.exe')
198    
199    def exists(env):
200      """
201      Is NSIS findable on this machine?
202      """
203      if find_nsis(env) != None:
204        return 1
205      return 0

Legend:
Removed from v.578  
changed lines
  Added in v.1408

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22