3 ### Utility module for Python build systems
5 ### (c) 2009 Straylight/Edgeware
8 ###----- Licensing notice ---------------------------------------------------
10 ### This file is part of the Common Files Distribution (`common')
12 ### `Common' 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 ### `Common' 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 `common'; if not, write to the Free Software Foundation,
24 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 from __future__
import with_statement
31 import subprocess
as SUB
33 import distutils
.core
as DC
35 ###--------------------------------------------------------------------------
40 Return a list of the elements of SEQ, with duplicates removed.
42 Only the first occurrence (according to `==') is left.
52 ###--------------------------------------------------------------------------
53 ### Subprocess hacking.
55 class SubprocessFailure (Exception):
56 def __init__(me
, file, rc
):
62 return '%s failed (rc = %d)' %
(me
.file, WEXITSTATUS(me
.rc
))
63 elif WIFSIGNALED(me
.rc
):
64 return '%s died (signal %d)' %
(me
.file, WTERMSIG(me
.rc
))
66 return '%s died inexplicably' %
(me
.file)
68 def progoutput(command
):
70 Run the shell COMMAND and return its standard output.
72 The COMMAND must produce exactly one line of output, and must exit with
75 kid
= SUB
.Popen(command
, stdout
= SUB
.PIPE
)
76 out
= kid
.stdout
.readline()
77 junk
= kid
.stdout
.read()
80 "Child process `%s' produced unspected output %r" %
(command
, junk
)
83 raise SubprocessFailure
, (command
, rc
)
84 return out
.rstrip('\n')
86 ###--------------------------------------------------------------------------
87 ### External library packages.
93 def pkg_config(pkg
, version
):
95 Find the external package PKG and store the necessary compiler flags.
97 The include-directory names are stored in INCLUDEDIRS; the
98 library-directory names are in LIBDIRS; and the library names themselves
101 spec
= '%s >= %s' %
(pkg
, version
)
102 def weird(what
, word
):
104 "Unexpected `%s' item `%s' from package `%s'" %
(what
, word
, pkg
)
105 for word
in progoutput(['pkg-config', '--cflags', spec
]).split():
106 if word
.startswith('-I'):
107 INCLUDEDIRS
.append(word
[2:])
109 weird('--cflags', word
)
110 for word
in progoutput(['pkg-config', '--libs', spec
]).split():
111 if word
.startswith('-L'):
112 LIBDIRS
.append(word
[2:])
113 elif word
.startswith('-l'):
114 LIBS
.append(word
[2:])
116 weird('--libs', word
)
118 ###--------------------------------------------------------------------------
119 ### Substituting variables in files.
121 def needs_update_p(target
, sources
):
123 Returns whether TARGET is out of date relative to its SOURCES.
125 If TARGET exists and was modified more recentently than any of its SOURCES
126 then it doesn't need updating.
128 if not OS
.path
.exists(target
):
130 t_target
= OS
.stat(target
).st_mtime
131 for source
in sources
:
132 if OS
.stat(source
).st_mtime
>= t_target
:
136 RX_SUBST
= RE
.compile(r
'\%(\w+)\%')
137 def derive(target
, source
, substmap
):
139 Derive TARGET from SOURCE by making simple substitutions.
141 The SOURCE may contain markers %FOO%; these are replaced by SUBSTMAP['FOO']
144 if not needs_update_p(target
, [source
]):
146 print "making `%s' from `%s'" %
(target
, source
)
147 temp
= target
+ '.new'
148 with
open(temp
, 'w') as ft
:
149 with
open(source
, 'r') as fs
:
151 ft
.write(RX_SUBST
.sub((lambda m
: substmap
[m
.group(1)]), line
))
152 OS
.rename(temp
, target
)
154 def generate(target
, source
= None):
156 Generate TARGET by running the SOURCE Python script.
158 If SOURCE is omitted, replace the extension of TARGET by `.py'.
161 source
= OS
.path
.splitext(target
)[0] + '.py'
162 if not needs_update_p(target
, [source
]):
164 print "making `%s' using `%s'" %
(target
, source
)
165 temp
= target
+ '.new'
166 with
open(temp
, 'w') as ft
:
167 rc
= SUB
.call([SYS
.executable
, source
], stdout
= ft
)
169 raise SubprocessFailure
, (source
, rc
)
170 OS
.rename(temp
, target
)
172 ###--------------------------------------------------------------------------
173 ### Discovering version numbers.
175 def auto_version(writep
= True):
177 Returns the package version number.
179 As a side-effect, if WRITEP is true, then write the version number to the
180 RELEASE file so that it gets included in distributions.
182 version
= progoutput(['./auto-version'])
184 with
open('RELEASE.new', 'w') as ft
: ft
.write('%s\n' % version
)
185 OS
.rename('RELEASE.new', 'RELEASE')
188 ###----- That's all, folks --------------------------------------------------