3 ### Utility module for Python build systems
5 ### (c) 2009 Straylight/Edgeware
8 ###----- Licensing notice ---------------------------------------------------
10 ### This file is part of the Python interface to mLib.
12 ### mLib/Python is free software; you can redistribute it and/or modify
13 ### it under the terms of the GNU General Public License as published by
14 ### the Free Software Foundation; either version 2 of the License, or
15 ### (at your option) any later version.
17 ### mLib/Python is distributed in the hope that it will be useful,
18 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ### GNU General Public License for more details.
22 ### You should have received a copy of the GNU General Public License
23 ### along with mLib/Python; if not, write to the Free Software Foundation,
24 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 import subprocess
as SUB
31 import distutils
.core
as DC
33 ###--------------------------------------------------------------------------
38 Return a list of the elements of SEQ, with duplicates removed.
40 Only the first occurrence (according to `==') is left.
50 ###--------------------------------------------------------------------------
51 ### Subprocess hacking.
53 class SubprocessFailure (Exception):
54 def __init__(me
, file, rc
):
60 return '%s failed (rc = %d)' %
(me
.file, WEXITSTATUS(me
.rc
))
61 elif WIFSIGNALED(me
.rc
):
62 return '%s died (signal %d)' %
(me
.file, WTERMSIG(me
.rc
))
64 return '%s died inexplicably' %
(me
.file)
66 def progoutput(command
):
68 Run the shell COMMAND and return its standard output.
70 The COMMAND must produce exactly one line of output, and must exit with
73 kid
= SUB
.Popen(command
, stdout
= SUB
.PIPE
)
74 out
= kid
.stdout
.readline()
75 junk
= kid
.stdout
.read()
78 "Child process `%s' produced unspected output %r" %
(command
, junk
)
81 raise SubprocessFailure
, (command
, rc
)
82 return out
.rstrip('\n')
84 ###--------------------------------------------------------------------------
85 ### External library packages.
91 def pkg_config(pkg
, version
):
93 Find the external package PKG and store the necessary compiler flags.
95 The include-directory names are stored in INCLUDEDIRS; the
96 library-directory names are in LIBDIRS; and the library names themselves
99 spec
= '%s >= %s' %
(pkg
, version
)
100 def weird(what
, word
):
102 "Unexpected `%s' item `%s' from package `%s'" %
(what
, word
, pkg
)
103 for word
in progoutput(['pkg-config', '--cflags', spec
]).split():
104 if word
.startswith('-I'):
105 INCLUDEDIRS
.append(word
[2:])
107 weird('--cflags', word
)
108 for word
in progoutput(['pkg-config', '--libs', spec
]).split():
109 if word
.startswith('-L'):
110 LIBDIRS
.append(word
[2:])
111 elif word
.startswith('-l'):
112 LIBS
.append(word
[2:])
114 weird('--libs', word
)
116 ###--------------------------------------------------------------------------
117 ### Substituting variables in files.
119 def needs_update_p(target
, sources
):
121 Returns whether TARGET is out of date relative to its SOURCES.
123 If TARGET exists and was modified more recentently than any of its SOURCES
124 then it doesn't need updating.
126 if not OS
.path
.exists(target
):
128 t_target
= OS
.stat(target
).st_mtime
129 for source
in sources
:
130 if OS
.stat(source
).st_mtime
>= t_target
:
134 RX_SUBST
= RE
.compile(r
'\%(\w+)\%')
135 def derive(target
, source
, substmap
):
137 Derive TARGET from SOURCE by making simple substitutions.
139 The SOURCE may contain markers %FOO%; these are replaced by SUBSTMAP['FOO']
142 if not needs_update_p(target
, [source
]):
144 print "making `%s' from `%s'" %
(target
, source
)
145 temp
= target
+ '.new'
148 fs
= open(source
, 'r')
151 ft
.write(RX_SUBST
.sub((lambda m
: substmap
[m
.group(1)]), line
))
156 OS
.rename(temp
, target
)
158 def generate(target
, source
= None):
160 Generate TARGET by running the SOURCE Python script.
162 If SOURCE is omitted, replace the extension of TARGET by `.py'.
165 source
= OS
.path
.splitext(target
)[0] + '.py'
166 if not needs_update_p(target
, [source
]):
168 print "making `%s' using `%s'" %
(target
, source
)
169 temp
= target
+ '.new'
172 rc
= SUB
.call([SYS
.executable
, source
], stdout
= ft
)
176 raise SubprocessFailure
, (source
, rc
)
177 OS
.rename(temp
, target
)
179 ###--------------------------------------------------------------------------
180 ### Discovering version numbers.
182 def auto_version(writep
= True):
184 Returns the package version number.
186 As a side-effect, if WRITEP is true, then write the version number to the
187 RELEASE file so that it gets included in distributions.
189 version
= progoutput(['./auto-version'])
191 ft
= open('RELEASE.new', 'w')
193 ft
.write('%s\n' % version
)
196 OS
.rename('RELEASE.new', 'RELEASE')
199 ###----- That's all, folks --------------------------------------------------