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