comparison mxtool/mx.py @ 5055:90c150a0a22b

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Thu, 08 Mar 2012 12:46:19 +0100
parents e2de9649f0a9
children 9d055af068a8
comparison
equal deleted inserted replaced
5054:b4f548d49f96 5055:90c150a0a22b
23 # or visit www.oracle.com if you need additional information or have any 23 # or visit www.oracle.com if you need additional information or have any
24 # questions. 24 # questions.
25 # 25 #
26 # ---------------------------------------------------------------------------------------------------- 26 # ----------------------------------------------------------------------------------------------------
27 # 27 #
28 # mx is a command line tool inspired by mvn (http://maven.apache.org/) 28
29 # and hg (http://mercurial.selenic.com/). It includes a mechanism for 29 r"""
30 # managing the dependencies between a set of projects (like Maven) 30 mx is a command line tool inspired by mvn (http://maven.apache.org/)
31 # as well as making it simple to run commands 31 and hg (http://mercurial.selenic.com/). It includes a mechanism for
32 # (like hg is the interface to the Mercurial commands). 32 managing the dependencies between a set of projects (like Maven)
33 # 33 as well as making it simple to run commands
34 # The organizing principle of mx is a project suite. A suite is a directory 34 (like hg is the interface to the Mercurial commands).
35 # containing one or more projects. It's not coincidental that this closely 35
36 # matches the layout of one or more projects in a Mercurial repository. 36 The organizing principle of mx is a project suite. A suite is a directory
37 # The configuration information for a suite lives in an 'mx' sub-directory 37 containing one or more projects. It's not coincidental that this closely
38 # at the top level of the suite. 38 matches the layout of one or more projects in a Mercurial repository.
39 # 39 The configuration information for a suite lives in an 'mx' sub-directory
40 # When launched, mx treats the current working directory as a suite. 40 at the top level of the suite.
41 # This is the primary suite. All other suites are called included suites. 41
42 # 42 When launched, mx treats the current working directory as a suite.
43 # The configuration files (i.e. in the 'mx' sub-directory) of a suite are: 43 This is the primary suite. All other suites are called included suites.
44 # 44
45 # projects - Defines the projects and libraries in the suite and the dependencies between them 45 The configuration files (i.e. in the 'mx' sub-directory) of a suite are:
46 # commands.py - Suite specific extensions to the commands available to mx. This is only processed 46
47 # for the primary suite. 47 projects
48 # includes - Other suites to be loaded. This is recursive. 48 Defines the projects and libraries in the suite and the
49 # env - A set of environment variable definitions. 49 dependencies between them.
50 # 50
51 # The includes and env files are typically not put under version control 51 commands.py
52 # as they usually contain local file-system paths. 52 Suite specific extensions to the commands available to mx.
53 # 53 This is only processed for the primary suite.
54 # The projects file is like the pom.xml file from Maven except that 54
55 # it is a properties file (not XML). Each non-comment line 55 includes
56 # in the file specifies an attribute of a project or library. The main 56 Other suites to be loaded. This is recursive.
57 # difference between a project and a library is that the former contains 57
58 # source code built by the mx tool where as the latter is an external 58 env
59 # dependency. The format of the projects file is 59 A set of environment variable definitions. These override any
60 # 60 existing environment variables.
61 # Library specification format: 61
62 # 62 The includes and env files are typically not put under version control
63 # library@<name>@<prop>=<value> 63 as they usually contain local file-system paths.
64 # 64
65 # Built-in library properties (* = required): 65 The projects file is like the pom.xml file from Maven except that
66 # 66 it is a properties file (not XML). Each non-comment line
67 # *path: the file system path for the library to appear on a class path 67 in the file specifies an attribute of a project or library. The main
68 # urls: a comma seperated list of URLs from which the library can be downloaded 68 difference between a project and a library is that the former contains
69 # optional: if "true" then this library will be omitted from a class path if it doesn't exist on the file system and no URLs are specified 69 source code built by the mx tool where as the latter is an external
70 # 70 dependency. The format of the projects file is
71 # Project specification format: 71
72 # 72 Library specification format:
73 # project@<name>@<prop>=<value> 73
74 # 74 library@<name>@<prop>=<value>
75 # The name of a project also denotes the directory it is in. 75
76 # 76 Built-in library properties (* = required):
77 # Built-in project properties: 77
78 # 78 *path
79 # *sourceDirs: a comma separated list of source directoriy names (relative to the project directory) 79 The file system path for the library to appear on a class path.
80 # dependencies: a comma separated list of the libraries and project the project depends upon (transitive dependencies may be omitted) 80
81 # checkstyle: the project whose Checkstyle configuration (i.e. <project>/.checkstyle_checks.xml) is used 81 urls
82 # 82 A comma separated list of URLs from which the library (jar) can
83 # Other properties can be specified for projects and libraries for use by extension commands. 83 be downloaded and saved in the location specified by 'path'.
84 # 84
85 # Values can use environment variables with Bash syntax (e.g. ${HOME}). 85 optional
86 If "true" then this library will be omitted from a class path
87 if it doesn't exist on the file system and no URLs are specified.
88
89 Project specification format:
90
91 project@<name>@<prop>=<value>
92
93 The name of a project also denotes the directory it is in.
94
95 Built-in project properties (* = required):
96
97 subDir
98 The sub-directory of the suite in which the project directory is
99 contained. If not specified, the project directory is directly
100 under the suite directory.
101
102 *sourceDirs
103 A comma separated list of source directory names (relative to
104 the project directory).
105
106 dependencies
107 A comma separated list of the libraries and project the project
108 depends upon (transitive dependencies should be omitted).
109
110 checkstyle
111 The project whose Checkstyle configuration
112 (i.e. <project>/.checkstyle_checks.xml) is used.
113
114 native
115 "true" if the project is native.
116
117 javaCompliance
118 The minimum JDK version (format: x.y) to which the project's
119 sources comply (required for non-native projects).
120
121 Other properties can be specified for projects and libraries for use
122 by extension commands.
123
124 Property values can use environment variables with Bash syntax (e.g. ${HOME}).
125 """
86 126
87 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal 127 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal
88 import shutil, fnmatch, re, xml.dom.minidom 128 import shutil, fnmatch, re, xml.dom.minidom
89 from collections import Callable 129 from collections import Callable
90 from threading import Thread 130 from threading import Thread
105 """ 145 """
106 class Dependency: 146 class Dependency:
107 def __init__(self, suite, name): 147 def __init__(self, suite, name):
108 self.name = name 148 self.name = name
109 self.suite = suite 149 self.suite = suite
110 150
111 def __str__(self): 151 def __str__(self):
112 return self.name 152 return self.name
113 153
114 def __eq__(self, other): 154 def __eq__(self, other):
115 return self.name == other.name 155 return self.name == other.name
116 156
117 def __ne__(self, other): 157 def __ne__(self, other):
118 return self.name != other.name 158 return self.name != other.name
119 159
120 def __hash__(self): 160 def __hash__(self):
121 return hash(self.name) 161 return hash(self.name)
122 162
123 def isLibrary(self): 163 def isLibrary(self):
124 return isinstance(self, Library) 164 return isinstance(self, Library)
125 165
126 class Project(Dependency): 166 class Project(Dependency):
127 def __init__(self, suite, name, srcDirs, deps, dir): 167 def __init__(self, suite, name, srcDirs, deps, javaCompliance, dir):
128 Dependency.__init__(self, suite, name) 168 Dependency.__init__(self, suite, name)
129 self.srcDirs = srcDirs 169 self.srcDirs = srcDirs
130 self.deps = deps 170 self.deps = deps
131 self.checkstyleProj = name 171 self.checkstyleProj = name
172 self.javaCompliance = JavaCompliance(javaCompliance) if javaCompliance is not None else None
132 self.native = False 173 self.native = False
133 self.dir = dir 174 self.dir = dir
134 175
135 def all_deps(self, deps, includeLibs, includeSelf=True): 176 def all_deps(self, deps, includeLibs, includeSelf=True):
136 """ 177 """
137 Add the transitive set of dependencies for this project, including 178 Add the transitive set of dependencies for this project, including
138 libraries if 'includeLibs' is true, to the 'deps' list. 179 libraries if 'includeLibs' is true, to the 'deps' list.
139 """ 180 """
150 if not dep in deps: 191 if not dep in deps:
151 dep.all_deps(deps, includeLibs) 192 dep.all_deps(deps, includeLibs)
152 if not self in deps and includeSelf: 193 if not self in deps and includeSelf:
153 deps.append(self) 194 deps.append(self)
154 return deps 195 return deps
155 196
156 def _compute_max_dep_distances(self, name, distances, dist): 197 def _compute_max_dep_distances(self, name, distances, dist):
157 currentDist = distances.get(name); 198 currentDist = distances.get(name);
158 if currentDist is None or currentDist < dist: 199 if currentDist is None or currentDist < dist:
159 distances[name] = dist 200 distances[name] = dist
160 p = project(name, False) 201 p = project(name, False)
161 if p is not None: 202 if p is not None:
162 for dep in p.deps: 203 for dep in p.deps:
163 self._compute_max_dep_distances(dep, distances, dist + 1) 204 self._compute_max_dep_distances(dep, distances, dist + 1)
164 205
165 def canonical_deps(self): 206 def canonical_deps(self):
166 """ 207 """
167 Get the dependencies of this project that are not recursive (i.e. cannot be reached 208 Get the dependencies of this project that are not recursive (i.e. cannot be reached
168 via other dependencies). 209 via other dependencies).
169 """ 210 """
172 self._compute_max_dep_distances(self.name, distances, 0) 213 self._compute_max_dep_distances(self.name, distances, 0)
173 for n,d in distances.iteritems(): 214 for n,d in distances.iteritems():
174 assert d > 0 or n == self.name 215 assert d > 0 or n == self.name
175 if d == 1: 216 if d == 1:
176 result.add(n) 217 result.add(n)
177 218
178 219
179 if len(result) == len(self.deps) and frozenset(self.deps) == result: 220 if len(result) == len(self.deps) and frozenset(self.deps) == result:
180 return self.deps 221 return self.deps
181 return result; 222 return result;
182 223
183 224
184 def source_dirs(self): 225 def source_dirs(self):
185 """ 226 """
186 Get the directories in which the sources of this project are found. 227 Get the directories in which the sources of this project are found.
187 """ 228 """
188 return [join(self.dir, s) for s in self.srcDirs] 229 return [join(self.dir, s) for s in self.srcDirs]
189 230
190 def output_dir(self): 231 def output_dir(self):
191 """ 232 """
192 Get the directory in which the class files of this project are found/placed. 233 Get the directory in which the class files of this project are found/placed.
193 """ 234 """
194 if self.native: 235 if self.native:
195 return None 236 return None
196 return join(self.dir, 'bin') 237 return join(self.dir, 'bin')
197 238
239 def jasmin_output_dir(self):
240 """
241 Get the directory in which the Jasmin assembled class files of this project are found/placed.
242 """
243 if self.native:
244 return None
245 return join(self.dir, 'jasmin_classes')
246
198 def append_to_classpath(self, cp, resolve): 247 def append_to_classpath(self, cp, resolve):
199 if not self.native: 248 if not self.native:
200 cp.append(self.output_dir()) 249 cp.append(self.output_dir())
201 250
202 class Library(Dependency): 251 class Library(Dependency):
203 def __init__(self, suite, name, path, mustExist, urls): 252 def __init__(self, suite, name, path, mustExist, urls):
204 Dependency.__init__(self, suite, name) 253 Dependency.__init__(self, suite, name)
205 self.path = path.replace('/', os.sep) 254 self.path = path.replace('/', os.sep)
206 self.urls = urls 255 self.urls = urls
207 self.mustExist = mustExist 256 self.mustExist = mustExist
208 257
209 def get_path(self, resolve): 258 def get_path(self, resolve):
210 path = self.path 259 path = self.path
211 if not isabs(path): 260 if not isabs(path):
212 path = join(self.suite.dir, path) 261 path = join(self.suite.dir, path)
213 if resolve and self.mustExist and not exists(path): 262 if resolve and self.mustExist and not exists(path):
214 assert not len(self.urls) == 0, 'cannot find required library ' + self.name + " " + path; 263 assert not len(self.urls) == 0, 'cannot find required library ' + self.name + " " + path;
215 print('Downloading ' + self.name + ' from ' + str(self.urls)) 264 print('Downloading ' + self.name + ' from ' + str(self.urls))
216 download(path, self.urls) 265 download(path, self.urls)
217 266
218 return path 267 return path
219 268
220 def append_to_classpath(self, cp, resolve): 269 def append_to_classpath(self, cp, resolve):
221 path = self.get_path(resolve) 270 path = self.get_path(resolve)
222 if exists(path) or not resolve: 271 if exists(path) or not resolve:
223 cp.append(path) 272 cp.append(path)
224 273
225 class Suite: 274 class Suite:
226 def __init__(self, dir, primary): 275 def __init__(self, dir, primary):
227 self.dir = dir 276 self.dir = dir
228 self.projects = [] 277 self.projects = []
229 self.libs = [] 278 self.libs = []
235 if primary: 284 if primary:
236 self._load_commands(mxDir) 285 self._load_commands(mxDir)
237 286
238 def _load_projects(self, mxDir): 287 def _load_projects(self, mxDir):
239 libsMap = dict() 288 libsMap = dict()
240 projsMap = dict() 289 projsMap = dict()
241 projectsFile = join(mxDir, 'projects') 290 projectsFile = join(mxDir, 'projects')
242 if not exists(projectsFile): 291 if not exists(projectsFile):
243 return 292 return
244 with open(projectsFile) as f: 293 with open(projectsFile) as f:
245 for line in f: 294 for line in f:
246 line = line.strip() 295 line = line.strip()
247 if len(line) != 0 and line[0] != '#': 296 if len(line) != 0 and line[0] != '#':
248 key, value = line.split('=', 1) 297 key, value = line.split('=', 1)
249 298
250 parts = key.split('@') 299 parts = key.split('@')
251 300
252 if len(parts) == 2: 301 if len(parts) == 2:
253 pass 302 pass
254 if len(parts) != 3: 303 if len(parts) != 3:
255 abort('Property name does not have 3 parts separated by "@": ' + key) 304 abort('Property name does not have 3 parts separated by "@": ' + key)
256 kind, name, attr = parts 305 kind, name, attr = parts
258 m = projsMap 307 m = projsMap
259 elif kind == 'library': 308 elif kind == 'library':
260 m = libsMap 309 m = libsMap
261 else: 310 else:
262 abort('Property name does not start with "project@" or "library@": ' + key) 311 abort('Property name does not start with "project@" or "library@": ' + key)
263 312
264 attrs = m.get(name) 313 attrs = m.get(name)
265 if attrs is None: 314 if attrs is None:
266 attrs = dict() 315 attrs = dict()
267 m[name] = attrs 316 m[name] = attrs
268 value = expandvars_in_property(value) 317 value = expandvars_in_property(value)
269 attrs[attr] = value 318 attrs[attr] = value
270 319
271 def pop_list(attrs, name): 320 def pop_list(attrs, name):
272 v = attrs.pop(name, None) 321 v = attrs.pop(name, None)
273 if v is None or len(v.strip()) == 0: 322 if v is None or len(v.strip()) == 0:
274 return [] 323 return []
275 return [n.strip() for n in v.split(',')] 324 return [n.strip() for n in v.split(',')]
276 325
277 for name, attrs in projsMap.iteritems(): 326 for name, attrs in projsMap.iteritems():
278 srcDirs = pop_list(attrs, 'sourceDirs') 327 srcDirs = pop_list(attrs, 'sourceDirs')
279 deps = pop_list(attrs, 'dependencies') 328 deps = pop_list(attrs, 'dependencies')
329 javaCompliance = attrs.pop('javaCompliance', None)
280 subDir = attrs.pop('subDir', None); 330 subDir = attrs.pop('subDir', None);
281 if subDir is None: 331 if subDir is None:
282 dir = join(self.dir, name) 332 dir = join(self.dir, name)
283 else: 333 else:
284 dir = join(self.dir, subDir, name) 334 dir = join(self.dir, subDir, name)
285 p = Project(self, name, srcDirs, deps, dir) 335 p = Project(self, name, srcDirs, deps, javaCompliance, dir)
286 p.checkstyleProj = attrs.pop('checkstyle', name) 336 p.checkstyleProj = attrs.pop('checkstyle', name)
287 p.native = attrs.pop('native', '') == 'true' 337 p.native = attrs.pop('native', '') == 'true'
338 if not p.native and p.javaCompliance is None:
339 abort('javaCompliance property required for non-native project ' + name)
288 p.__dict__.update(attrs) 340 p.__dict__.update(attrs)
289 self.projects.append(p) 341 self.projects.append(p)
290 342
291 for name, attrs in libsMap.iteritems(): 343 for name, attrs in libsMap.iteritems():
292 path = attrs.pop('path') 344 path = attrs.pop('path')
293 mustExist = attrs.pop('optional', 'false') != 'true' 345 mustExist = attrs.pop('optional', 'false') != 'true'
294 urls = pop_list(attrs, 'urls') 346 urls = pop_list(attrs, 'urls')
295 l = Library(self, name, path, mustExist, urls) 347 l = Library(self, name, path, mustExist, urls)
296 l.__dict__.update(attrs) 348 l.__dict__.update(attrs)
297 self.libs.append(l) 349 self.libs.append(l)
298 350
299 def _load_commands(self, mxDir): 351 def _load_commands(self, mxDir):
300 commands = join(mxDir, 'commands.py') 352 commands = join(mxDir, 'commands.py')
301 if exists(commands): 353 if exists(commands):
302 # temporarily extend the Python path 354 # temporarily extend the Python path
303 sys.path.insert(0, mxDir) 355 sys.path.insert(0, mxDir)
304 356
305 mod = __import__('commands') 357 mod = __import__('commands')
306 358
307 # revert the Python path 359 # revert the Python path
308 del sys.path[0] 360 del sys.path[0]
309 361
310 if not hasattr(mod, 'mx_init'): 362 if not hasattr(mod, 'mx_init'):
311 abort(commands + ' must define an mx_init(env) function') 363 abort(commands + ' must define an mx_init(env) function')
312 if hasattr(mod, 'mx_post_parse_cmd_line'): 364 if hasattr(mod, 'mx_post_parse_cmd_line'):
313 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line 365 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line
314 366
315 mod.mx_init() 367 mod.mx_init()
316 self.commands = mod 368 self.commands = mod
317 369
318 def _load_includes(self, mxDir): 370 def _load_includes(self, mxDir):
319 includes = join(mxDir, 'includes') 371 includes = join(mxDir, 'includes')
320 if exists(includes): 372 if exists(includes):
321 with open(includes) as f: 373 with open(includes) as f:
322 for line in f: 374 for line in f:
323 self.includes.append(expandvars_in_property(line.strip())) 375 self.includes.append(expandvars_in_property(line.strip()))
324 376
325 def _load_env(self, mxDir): 377 def _load_env(self, mxDir):
326 e = join(mxDir, 'env') 378 e = join(mxDir, 'env')
327 if exists(e): 379 if exists(e):
328 with open(e) as f: 380 with open(e) as f:
329 for line in f: 381 for line in f:
330 line = line.strip() 382 line = line.strip()
331 if len(line) != 0 and line[0] != '#': 383 if len(line) != 0 and line[0] != '#':
332 key, value = line.split('=', 1) 384 key, value = line.split('=', 1)
333 os.environ[key.strip()] = expandvars_in_property(value.strip()) 385 os.environ[key.strip()] = expandvars_in_property(value.strip())
334 386
335 def _post_init(self, opts): 387 def _post_init(self, opts):
336 mxDir = join(self.dir, 'mx') 388 mxDir = join(self.dir, 'mx')
337 self._load_includes(mxDir) 389 self._load_includes(mxDir)
338 self._load_projects(mxDir) 390 self._load_projects(mxDir)
339 if self.mx_post_parse_cmd_line is not None: 391 if self.mx_post_parse_cmd_line is not None:
346 for l in self.libs: 398 for l in self.libs:
347 existing = _libs.get(l.name) 399 existing = _libs.get(l.name)
348 if existing is not None: 400 if existing is not None:
349 abort('cannot redefine library ' + l.name) 401 abort('cannot redefine library ' + l.name)
350 _libs[l.name] = l 402 _libs[l.name] = l
351 403
352 def get_os(): 404 def get_os():
353 """ 405 """
354 Get a canonical form of sys.platform. 406 Get a canonical form of sys.platform.
355 """ 407 """
356 if sys.platform.startswith('darwin'): 408 if sys.platform.startswith('darwin'):
369 if not exists(mxDir) or not isdir(mxDir): 421 if not exists(mxDir) or not isdir(mxDir):
370 return None 422 return None
371 if not _suites.has_key(dir): 423 if not _suites.has_key(dir):
372 suite = Suite(dir, primary) 424 suite = Suite(dir, primary)
373 _suites[dir] = suite 425 _suites[dir] = suite
374 return suite 426 return suite
375 427
376 def suites(): 428 def suites():
377 """ 429 """
378 Get the list of all loaded suites. 430 Get the list of all loaded suites.
379 """ 431 """
382 def projects(): 434 def projects():
383 """ 435 """
384 Get the list of all loaded projects. 436 Get the list of all loaded projects.
385 """ 437 """
386 return _projects.values() 438 return _projects.values()
387 439
388 def project(name, fatalIfMissing=True): 440 def project(name, fatalIfMissing=True):
389 """ 441 """
390 Get the project for a given name. This will abort if the named project does 442 Get the project for a given name. This will abort if the named project does
391 not exist and 'fatalIfMissing' is true. 443 not exist and 'fatalIfMissing' is true.
392 """ 444 """
419 """ 471 """
420 Get the class path for a list of given projects, resolving each entry in the 472 Get the class path for a list of given projects, resolving each entry in the
421 path (e.g. downloading a missing library) if 'resolve' is true. 473 path (e.g. downloading a missing library) if 'resolve' is true.
422 """ 474 """
423 if names is None: 475 if names is None:
424 return _as_classpath(sorted_deps(True), resolve) 476 return _as_classpath(sorted_deps(includeLibs=True), resolve)
425 deps = [] 477 deps = []
426 if isinstance(names, types.StringTypes): 478 if isinstance(names, types.StringTypes):
427 project(names).all_deps(deps, True, includeSelf) 479 project(names).all_deps(deps, True, includeSelf)
428 else: 480 else:
429 for n in names: 481 for n in names:
430 project(n).all_deps(deps, True, includeSelf) 482 project(n).all_deps(deps, True, includeSelf)
431 return _as_classpath(deps, resolve) 483 return _as_classpath(deps, resolve)
432 484
433 def sorted_deps(includeLibs=False): 485 def sorted_deps(projectNames=None, includeLibs=False):
434 """ 486 """
435 Gets the loaded projects and libraries sorted such that dependencies 487 Gets projects and libraries sorted such that dependencies
436 are before the projects that depend on them. Unless 'includeLibs' is 488 are before the projects that depend on them. Unless 'includeLibs' is
437 true, libraries are omitted from the result. 489 true, libraries are omitted from the result.
438 """ 490 """
439 deps = [] 491 deps = []
440 for p in _projects.itervalues(): 492 if projectNames is None:
493 projects = _projects.values()
494 else:
495 projects = [project(name) for name in projectNames]
496
497 for p in projects:
441 p.all_deps(deps, includeLibs) 498 p.all_deps(deps, includeLibs)
442 return deps 499 return deps
443 500
444 class ArgParser(ArgumentParser): 501 class ArgParser(ArgumentParser):
445 502
446 # Override parent to append the list of available commands 503 # Override parent to append the list of available commands
447 def format_help(self): 504 def format_help(self):
448 return ArgumentParser.format_help(self) + _format_commands() 505 return ArgumentParser.format_help(self) + _format_commands()
449 506
450 507
451 def __init__(self): 508 def __init__(self):
452 self.java_initialized = False 509 self.java_initialized = False
453 ArgumentParser.__init__(self, prog='mx') 510 ArgumentParser.__init__(self, prog='mx')
454 511
455 self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output') 512 self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output')
456 self.add_argument('-d', action='store_true', dest='java_dbg', help='make Java processes wait on port 8000 for a debugger') 513 self.add_argument('--dbg', type=int, dest='java_dbg_port', help='make Java processes wait on <port> for a debugger', metavar='<port>')
514 self.add_argument('-d', action='store_const', const=8000, dest='java_dbg_port', help='alias for "-dbg 8000"')
457 self.add_argument('--cp-pfx', dest='cp_prefix', help='class path prefix', metavar='<arg>') 515 self.add_argument('--cp-pfx', dest='cp_prefix', help='class path prefix', metavar='<arg>')
458 self.add_argument('--cp-sfx', dest='cp_suffix', help='class path suffix', metavar='<arg>') 516 self.add_argument('--cp-sfx', dest='cp_suffix', help='class path suffix', metavar='<arg>')
459 self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@<args>', default=DEFAULT_JAVA_ARGS) 517 self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@<args>', default=DEFAULT_JAVA_ARGS)
460 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) 518 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[])
461 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) 519 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[])
463 self.add_argument('--java-home', help='JDK installation directory (must be JDK 6 or later)', metavar='<path>') 521 self.add_argument('--java-home', help='JDK installation directory (must be JDK 6 or later)', metavar='<path>')
464 if get_os() != 'windows': 522 if get_os() != 'windows':
465 # Time outs are (currently) implemented with Unix specific functionality 523 # Time outs are (currently) implemented with Unix specific functionality
466 self.add_argument('--timeout', help='Timeout (in seconds) for command', type=int, default=0, metavar='<secs>') 524 self.add_argument('--timeout', help='Timeout (in seconds) for command', type=int, default=0, metavar='<secs>')
467 self.add_argument('--ptimeout', help='Timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') 525 self.add_argument('--ptimeout', help='Timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>')
468 526
469 def _parse_cmd_line(self, args=None): 527 def _parse_cmd_line(self, args=None):
470 if args is None: 528 if args is None:
471 args = sys.argv[1:] 529 args = sys.argv[1:]
472 530
473 self.add_argument('commandAndArgs', nargs=REMAINDER, metavar='command args...') 531 self.add_argument('commandAndArgs', nargs=REMAINDER, metavar='command args...')
474 532
475 opts = self.parse_args() 533 opts = self.parse_args()
476 534
477 # Give the timeout options a default value to avoid the need for hasattr() tests 535 # Give the timeout options a default value to avoid the need for hasattr() tests
478 opts.__dict__.setdefault('timeout', 0) 536 opts.__dict__.setdefault('timeout', 0)
479 opts.__dict__.setdefault('ptimeout', 0) 537 opts.__dict__.setdefault('ptimeout', 0)
480 538
481 if opts.java_home is None: 539 if opts.java_home is None:
484 if opts.java_home is None or opts.java_home == '': 542 if opts.java_home is None or opts.java_home == '':
485 abort('Could not find Java home. Use --java-home option or ensure JAVA_HOME environment variable is set.') 543 abort('Could not find Java home. Use --java-home option or ensure JAVA_HOME environment variable is set.')
486 544
487 if opts.user_home is None or opts.user_home == '': 545 if opts.user_home is None or opts.user_home == '':
488 abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.') 546 abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.')
489 547
490 os.environ['JAVA_HOME'] = opts.java_home 548 os.environ['JAVA_HOME'] = opts.java_home
491 os.environ['HOME'] = opts.user_home 549 os.environ['HOME'] = opts.user_home
492 550
493 commandAndArgs = opts.__dict__.pop('commandAndArgs') 551 commandAndArgs = opts.__dict__.pop('commandAndArgs')
494 return opts, commandAndArgs 552 return opts, commandAndArgs
495 553
496 def _format_commands(): 554 def _format_commands():
497 msg = '\navailable commands:\n\n' 555 msg = '\navailable commands:\n\n'
498 for cmd in sorted(commands.iterkeys()): 556 for cmd in sorted(commands.iterkeys()):
499 c, _ = commands[cmd][:2] 557 c, _ = commands[cmd][:2]
500 doc = c.__doc__ 558 doc = c.__doc__
529 return os.waitpid(pid, os.WNOHANG) 587 return os.waitpid(pid, os.WNOHANG)
530 except OSError, e: 588 except OSError, e:
531 if e.errno == errno.EINTR: 589 if e.errno == errno.EINTR:
532 continue 590 continue
533 raise 591 raise
534 592
535 def _returncode(status): 593 def _returncode(status):
536 if os.WIFSIGNALED(status): 594 if os.WIFSIGNALED(status):
537 return -os.WTERMSIG(status) 595 return -os.WTERMSIG(status)
538 elif os.WIFEXITED(status): 596 elif os.WIFEXITED(status):
539 return os.WEXITSTATUS(status) 597 return os.WEXITSTATUS(status)
540 else: 598 else:
541 # Should never happen 599 # Should never happen
542 raise RuntimeError("Unknown child exit status!") 600 raise RuntimeError("Unknown child exit status!")
543 601
544 end = time.time() + timeout 602 end = time.time() + timeout
545 delay = 0.0005 603 delay = 0.0005
546 while True: 604 while True:
547 (pid, status) = _waitpid(process.pid) 605 (pid, status) = _waitpid(process.pid)
548 if pid == process.pid: 606 if pid == process.pid:
574 If the exit status is non-zero and `nonZeroIsFatal` is true, then mx is exited with 632 If the exit status is non-zero and `nonZeroIsFatal` is true, then mx is exited with
575 the same exit status. 633 the same exit status.
576 Each line of the standard output and error streams of the subprocess are redirected to 634 Each line of the standard output and error streams of the subprocess are redirected to
577 out and err if they are callable objects. 635 out and err if they are callable objects.
578 """ 636 """
579 637
580 assert isinstance(args, types.ListType), "'args' must be a list: " + str(args) 638 assert isinstance(args, types.ListType), "'args' must be a list: " + str(args)
581 for arg in args: 639 for arg in args:
582 assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg) 640 assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg)
583 641
584 if _opts.verbose: 642 if _opts.verbose:
585 log(' '.join(args)) 643 log(' '.join(args))
586 644
587 if timeout is None and _opts.ptimeout != 0: 645 if timeout is None and _opts.ptimeout != 0:
588 timeout = _opts.ptimeout 646 timeout = _opts.ptimeout
589 647
590 global _currentSubprocess 648 global _currentSubprocess
591 649
592 try: 650 try:
593 # On Unix, the new subprocess should be in a separate group so that a timeout alarm 651 # On Unix, the new subprocess should be in a separate group so that a timeout alarm
594 # can use os.killpg() to kill the whole subprocess group 652 # can use os.killpg() to kill the whole subprocess group
595 preexec_fn = None 653 preexec_fn = None
596 creationflags = 0 654 creationflags = 0
597 if get_os() == 'windows': 655 if get_os() == 'windows':
598 creationflags = subprocess.CREATE_NEW_PROCESS_GROUP 656 creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
599 else: 657 else:
600 preexec_fn = os.setsid 658 preexec_fn = os.setsid
601 659
602 if not callable(out) and not callable(err) and timeout is None: 660 if not callable(out) and not callable(err) and timeout is None:
603 # The preexec_fn=os.setsid 661 # The preexec_fn=os.setsid
604 p = subprocess.Popen(args, cwd=cwd, preexec_fn=preexec_fn, creationflags=creationflags) 662 p = subprocess.Popen(args, cwd=cwd, preexec_fn=preexec_fn, creationflags=creationflags)
605 _currentSubprocess = (p, args) 663 _currentSubprocess = (p, args)
606 retcode = waitOn(p) 664 retcode = waitOn(p)
607 else: 665 else:
608 def redirect(stream, f): 666 def redirect(stream, f):
609 for line in iter(stream.readline, ''): 667 for line in iter(stream.readline, ''):
610 f(line) 668 f(line)
611 stream.close() 669 stream.close()
639 697
640 if retcode and nonZeroIsFatal: 698 if retcode and nonZeroIsFatal:
641 if _opts.verbose: 699 if _opts.verbose:
642 raise subprocess.CalledProcessError(retcode, ' '.join(args)) 700 raise subprocess.CalledProcessError(retcode, ' '.join(args))
643 abort(retcode) 701 abort(retcode)
644 702
645 return retcode 703 return retcode
646 704
647 def exe_suffix(name): 705 def exe_suffix(name):
648 """ 706 """
649 Gets the platform specific suffix for an executable 707 Gets the platform specific suffix for an executable
650 """ 708 """
651 if get_os() == 'windows': 709 if get_os() == 'windows':
652 return name + '.exe' 710 return name + '.exe'
653 return name 711 return name
654 712
662 if os == 'linux': 720 if os == 'linux':
663 return name + '.so' 721 return name + '.so'
664 return name 722 return name
665 723
666 """ 724 """
725 A JavaCompliance simplifies comparing Java compliance values extracted from a JDK version string.
726 """
727 class JavaCompliance:
728 def __init__(self, ver):
729 m = re.match('1\.(\d+).*', ver)
730 assert m is not None, 'not a recognized version string: ' + vstring
731 self.value = int(m.group(1))
732
733 def __str__ (self):
734 return '1.' + str(self.value)
735
736 def __cmp__ (self, other):
737 if isinstance(other, types.StringType):
738 other = JavaCompliance(other)
739
740 return cmp(self.value, other.value)
741
742 """
667 A JavaConfig object encapsulates info on how Java commands are run. 743 A JavaConfig object encapsulates info on how Java commands are run.
668 """ 744 """
669 class JavaConfig: 745 class JavaConfig:
670 def __init__(self, opts): 746 def __init__(self, opts):
671 self.jdk = opts.java_home 747 self.jdk = opts.java_home
672 self.debug = opts.java_dbg 748 self.debug_port = opts.java_dbg_port
673 self.java = exe_suffix(join(self.jdk, 'bin', 'java')) 749 self.java = exe_suffix(join(self.jdk, 'bin', 'java'))
674 self.javac = exe_suffix(join(self.jdk, 'bin', 'javac')) 750 self.javac = exe_suffix(join(self.jdk, 'bin', 'javac'))
675 self.javap = exe_suffix(join(self.jdk, 'bin', 'javap')) 751 self.javap = exe_suffix(join(self.jdk, 'bin', 'javap'))
676 752
677 if not exists(self.java): 753 if not exists(self.java):
678 abort('Java launcher derived from JAVA_HOME does not exist: ' + self.java) 754 abort('Java launcher derived from JAVA_HOME does not exist: ' + self.java)
679 755
680 def delAtAndSplit(s): 756 def delAtAndSplit(s):
681 return shlex.split(s.lstrip('@')) 757 return shlex.split(s.lstrip('@'))
682 758
683 self.java_args = delAtAndSplit(_opts.java_args) 759 self.java_args = delAtAndSplit(_opts.java_args)
684 self.java_args_pfx = sum(map(delAtAndSplit, _opts.java_args_pfx), []) 760 self.java_args_pfx = sum(map(delAtAndSplit, _opts.java_args_pfx), [])
685 self.java_args_sfx = sum(map(delAtAndSplit, _opts.java_args_sfx), []) 761 self.java_args_sfx = sum(map(delAtAndSplit, _opts.java_args_sfx), [])
686 762
687 # Prepend the -d64 VM option only if the java command supports it 763 # Prepend the -d64 VM option only if the java command supports it
688 try: 764 try:
689 output = subprocess.check_output([self.java, '-d64', '-version'], stderr=subprocess.STDOUT) 765 output = subprocess.check_output([self.java, '-d64', '-version'], stderr=subprocess.STDOUT)
690 self.java_args = ['-d64'] + self.java_args 766 self.java_args = ['-d64'] + self.java_args
691 except subprocess.CalledProcessError as e: 767 except subprocess.CalledProcessError as e:
692 try: 768 try:
693 output = subprocess.check_output([self.java, '-version'], stderr=subprocess.STDOUT) 769 output = subprocess.check_output([self.java, '-version'], stderr=subprocess.STDOUT)
694 except subprocess.CalledProcessError as e: 770 except subprocess.CalledProcessError as e:
695 print e.output 771 print e.output
696 abort(e.returncode) 772 abort(e.returncode)
697 773
698 output = output.split() 774 output = output.split()
699 assert output[1] == 'version' 775 assert output[1] == 'version'
700 self.version = output[2].strip('"') 776 self.version = output[2].strip('"')
701 777 self.javaCompliance = JavaCompliance(self.version)
702 if self.debug: 778
703 self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000'] 779 if self.debug_port is not None:
780 self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(self.debug_port)]
704 781
705 def format_cmd(self, args): 782 def format_cmd(self, args):
706 return [self.java] + self.java_args_pfx + self.java_args + self.java_args_sfx + args 783 return [self.java] + self.java_args_pfx + self.java_args + self.java_args_sfx + args
707 784
708 def check_get_env(key): 785 def check_get_env(key):
709 """ 786 """
710 Gets an environment variable, aborting with a useful message if it is not set. 787 Gets an environment variable, aborting with a useful message if it is not set.
711 """ 788 """
712 value = get_env(key) 789 value = get_env(key)
723 800
724 def log(msg=None): 801 def log(msg=None):
725 """ 802 """
726 Write a message to the console. 803 Write a message to the console.
727 All script output goes through this method thus allowing a subclass 804 All script output goes through this method thus allowing a subclass
728 to redirect it. 805 to redirect it.
729 """ 806 """
730 if msg is None: 807 if msg is None:
731 print 808 print
732 else: 809 else:
733 print msg 810 print msg
738 if part.startswith('@'): 815 if part.startswith('@'):
739 cp += classpath(part[1:]).split(os.pathsep) 816 cp += classpath(part[1:]).split(os.pathsep)
740 else: 817 else:
741 cp.append(part) 818 cp.append(part)
742 return os.pathsep.join(cp) 819 return os.pathsep.join(cp)
743 820
744 def expand_project_in_args(args): 821 def expand_project_in_args(args):
745 for i in range(len(args)): 822 for i in range(len(args)):
746 if args[i] == '-cp' or args[i] == '-classpath': 823 if args[i] == '-cp' or args[i] == '-classpath':
747 if i + 1 < len(args): 824 if i + 1 < len(args):
748 args[i + 1] = expand_project_in_class_path_arg(args[i + 1]) 825 args[i + 1] = expand_project_in_class_path_arg(args[i + 1])
763 result = expandvars(value) 840 result = expandvars(value)
764 if '$' in result or '%' in result: 841 if '$' in result or '%' in result:
765 abort('Property contains an undefined environment variable: ' + value) 842 abort('Property contains an undefined environment variable: ' + value)
766 return result 843 return result
767 844
768 845
769 def abort(codeOrMessage): 846 def abort(codeOrMessage):
770 """ 847 """
771 Aborts the program with a SystemExit exception. 848 Aborts the program with a SystemExit exception.
772 If 'codeOrMessage' is a plain integer, it specifies the system exit status; 849 If 'codeOrMessage' is a plain integer, it specifies the system exit status;
773 if it is None, the exit status is zero; if it has another type (such as a string), 850 if it is None, the exit status is zero; if it has another type (such as a string),
774 the object's value is printed and the exit status is one. 851 the object's value is printed and the exit status is one.
775 """ 852 """
776 853
777 #import traceback 854 #import traceback
778 #traceback.print_stack() 855 #traceback.print_stack()
779 currentSubprocess = _currentSubprocess 856 currentSubprocess = _currentSubprocess
780 if currentSubprocess is not None: 857 if currentSubprocess is not None:
781 p, _ = currentSubprocess 858 p, _ = currentSubprocess
782 if get_os() == 'windows': 859 if get_os() == 'windows':
783 p.kill() 860 p.kill()
784 else: 861 else:
785 _kill_process_group(p.pid) 862 _kill_process_group(p.pid)
786 863
787 raise SystemExit(codeOrMessage) 864 raise SystemExit(codeOrMessage)
788 865
789 def download(path, urls, verbose=False): 866 def download(path, urls, verbose=False):
790 """ 867 """
791 Attempts to downloads content for each URL in a list, stopping after the first successful download. 868 Attempts to downloads content for each URL in a list, stopping after the first successful download.
793 is written to the file indicated by 'path'. 870 is written to the file indicated by 'path'.
794 """ 871 """
795 d = dirname(path) 872 d = dirname(path)
796 if d != '' and not exists(d): 873 if d != '' and not exists(d):
797 os.makedirs(d) 874 os.makedirs(d)
798 875
799 # Try it with the Java tool first since it can show a progress counter 876 # Try it with the Java tool first since it can show a progress counter
800 myDir = dirname(__file__) 877 myDir = dirname(__file__)
801 878
802 javaSource = join(myDir, 'URLConnectionDownload.java') 879 javaSource = join(myDir, 'URLConnectionDownload.java')
803 javaClass = join(myDir, 'URLConnectionDownload.class') 880 javaClass = join(myDir, 'URLConnectionDownload.class')
804 if not exists(javaClass) or getmtime(javaClass) < getmtime(javaSource): 881 if not exists(javaClass) or getmtime(javaClass) < getmtime(javaSource):
805 subprocess.check_call([java().javac, '-d', myDir, javaSource]) 882 subprocess.check_call([java().javac, '-d', myDir, javaSource])
806 if run([java().java, '-cp', myDir, 'URLConnectionDownload', path] + urls) == 0: 883 if run([java().java, '-cp', myDir, 'URLConnectionDownload', path] + urls) == 0:
807 return 884 return
808 885
809 def url_open(url): 886 def url_open(url):
810 userAgent = 'Mozilla/5.0 (compatible)' 887 userAgent = 'Mozilla/5.0 (compatible)'
811 headers = { 'User-Agent' : userAgent } 888 headers = { 'User-Agent' : userAgent }
812 req = urllib2.Request(url, headers=headers) 889 req = urllib2.Request(url, headers=headers)
813 return urllib2.urlopen(req); 890 return urllib2.urlopen(req);
814 891
815 for url in urls: 892 for url in urls:
816 try: 893 try:
817 if (verbose): 894 if (verbose):
818 log('Downloading ' + url + ' to ' + path) 895 log('Downloading ' + url + ' to ' + path)
819 if url.startswith('zip:') or url.startswith('jar:'): 896 if url.startswith('zip:') or url.startswith('jar:'):
822 abort('Zip or jar URL does not contain "!/": ' + url) 899 abort('Zip or jar URL does not contain "!/": ' + url)
823 url, _, entry = url[len('zip:'):].partition('!/') 900 url, _, entry = url[len('zip:'):].partition('!/')
824 with contextlib.closing(url_open(url)) as f: 901 with contextlib.closing(url_open(url)) as f:
825 data = f.read() 902 data = f.read()
826 zipdata = StringIO.StringIO(f.read()) 903 zipdata = StringIO.StringIO(f.read())
827 904
828 zf = zipfile.ZipFile(zipdata, 'r') 905 zf = zipfile.ZipFile(zipdata, 'r')
829 data = zf.read(entry) 906 data = zf.read(entry)
830 with open(path, 'wb') as f: 907 with open(path, 'wb') as f:
831 f.write(data) 908 f.write(data)
832 else: 909 else:
837 return 914 return
838 except IOError as e: 915 except IOError as e:
839 log('Error reading from ' + url + ': ' + str(e)) 916 log('Error reading from ' + url + ': ' + str(e))
840 except zipfile.BadZipfile as e: 917 except zipfile.BadZipfile as e:
841 log('Error in zip file downloaded from ' + url + ': ' + str(e)) 918 log('Error in zip file downloaded from ' + url + ': ' + str(e))
842 919
843 abort('Could not download to ' + path + ' from any of the following URLs:\n\n ' + 920 abort('Could not download to ' + path + ' from any of the following URLs:\n\n ' +
844 '\n '.join(urls) + '\n\nPlease use a web browser to do the download manually') 921 '\n '.join(urls) + '\n\nPlease use a web browser to do the download manually')
845 922
846 def update_file(path, content): 923 def update_file(path, content):
847 """ 924 """
848 Updates a file with some given content if the content differs from what's in 925 Updates a file with some given content if the content differs from what's in
849 the file already. The return value indicates if the file was updated. 926 the file already. The return value indicates if the file was updated.
850 """ 927 """
852 try: 929 try:
853 old = None 930 old = None
854 if existed: 931 if existed:
855 with open(path, 'rb') as f: 932 with open(path, 'rb') as f:
856 old = f.read() 933 old = f.read()
857 934
858 if old == content: 935 if old == content:
859 return False 936 return False
860 937
861 with open(path, 'wb') as f: 938 with open(path, 'wb') as f:
862 f.write(content) 939 f.write(content)
863 940
864 log(('modified ' if existed else 'created ') + path) 941 log(('modified ' if existed else 'created ') + path)
865 return True; 942 return True;
866 except IOError as e: 943 except IOError as e:
867 abort('Error while writing to ' + path + ': ' + str(e)); 944 abort('Error while writing to ' + path + ': ' + str(e));
868 945
869 # Builtin commands 946 # Builtin commands
870 947
871 def build(args, parser=None): 948 def build(args, parser=None):
872 """compile the Java and C sources, linking the latter 949 """compile the Java and C sources, linking the latter
873 950
874 Compile all the Java source code using the appropriate compilers 951 Compile all the Java source code using the appropriate compilers
875 and linkers for the various source code types.""" 952 and linkers for the various source code types."""
876 953
877 suppliedParser = parser is not None 954 suppliedParser = parser is not None
878 if not suppliedParser: 955 if not suppliedParser:
879 parser = ArgumentParser(prog='mx build') 956 parser = ArgumentParser(prog='mx build')
880 957
958 javaCompliance = java().javaCompliance
959
881 parser = parser if parser is not None else ArgumentParser(prog='mx build') 960 parser = parser if parser is not None else ArgumentParser(prog='mx build')
882 parser.add_argument('-f', action='store_true', dest='force', help='force compilation even if class files are up to date') 961 parser.add_argument('-f', action='store_true', dest='force', help='force compilation even if class files are up to date')
883 parser.add_argument('-c', action='store_true', dest='clean', help='removes existing build output') 962 parser.add_argument('-c', action='store_true', dest='clean', help='removes existing build output')
884 parser.add_argument('--source', dest='compliance', help='Java compliance level', default='1.6') 963 parser.add_argument('--source', dest='compliance', help='Java compliance level', default=str(javaCompliance))
885 parser.add_argument('--Wapi', action='store_true', dest='warnAPI', help='show warnings about using internal APIs') 964 parser.add_argument('--Wapi', action='store_true', dest='warnAPI', help='show warnings about using internal APIs')
965 parser.add_argument('--projects', action='store', help='comma separated projects to build (omit to build all projects)')
886 parser.add_argument('--no-java', action='store_false', dest='java', help='do not build Java projects') 966 parser.add_argument('--no-java', action='store_false', dest='java', help='do not build Java projects')
887 parser.add_argument('--no-native', action='store_false', dest='native', help='do not build native projects') 967 parser.add_argument('--no-native', action='store_false', dest='native', help='do not build native projects')
888 parser.add_argument('--jdt', help='Eclipse installation or path to ecj.jar for using the Eclipse batch compiler instead of javac', metavar='<path>') 968 parser.add_argument('--jdt', help='Eclipse installation or path to ecj.jar for using the Eclipse batch compiler instead of javac', metavar='<path>')
889 969
890 if suppliedParser: 970 if suppliedParser:
891 parser.add_argument('remainder', nargs=REMAINDER, metavar='...') 971 parser.add_argument('remainder', nargs=REMAINDER, metavar='...')
892 972
893 args = parser.parse_args(args) 973 args = parser.parse_args(args)
894 974
895 jdtJar = None 975 jdtJar = None
896 if args.jdt is not None: 976 if args.jdt is not None:
897 if args.jdt.endswith('.jar'): 977 if args.jdt.endswith('.jar'):
898 jdtJar=args.jdt 978 jdtJar=args.jdt
899 elif isdir(args.jdt): 979 elif isdir(args.jdt):
901 choices = [f for f in os.listdir(plugins) if fnmatch.fnmatch(f, 'org.eclipse.jdt.core_*.jar')] 981 choices = [f for f in os.listdir(plugins) if fnmatch.fnmatch(f, 'org.eclipse.jdt.core_*.jar')]
902 if len(choices) != 0: 982 if len(choices) != 0:
903 jdtJar = join(plugins, sorted(choices, reverse=True)[0]) 983 jdtJar = join(plugins, sorted(choices, reverse=True)[0])
904 984
905 built = set() 985 built = set()
906 for p in sorted_deps(): 986
907 987 projects = None
988 if args.projects is not None:
989 projects = args.projects.split(',')
990
991 for p in sorted_deps(projects):
908 if p.native: 992 if p.native:
909 if args.native: 993 if args.native:
910 log('Calling GNU make {0}...'.format(p.dir)) 994 log('Calling GNU make {0}...'.format(p.dir))
911 995
912 if args.clean: 996 if args.clean:
913 run([gmake_cmd(), 'clean'], cwd=p.dir) 997 run([gmake_cmd(), 'clean'], cwd=p.dir)
914 998
915 run([gmake_cmd()], cwd=p.dir) 999 run([gmake_cmd()], cwd=p.dir)
916 built.add(p.name) 1000 built.add(p.name)
917 continue 1001 continue
918 else: 1002 else:
919 if not args.java: 1003 if not args.java:
920 continue 1004 continue
921 1005
922 1006 # skip building this Java project if its Java compliance level is "higher" than the configured JDK
1007 if javaCompliance < p.javaCompliance:
1008 log('Excluding {0} from build (Java compliance level {1} required)'.format(p.name, p.javaCompliance))
1009 continue
1010
1011
923 outputDir = p.output_dir() 1012 outputDir = p.output_dir()
924 if exists(outputDir): 1013 if exists(outputDir):
925 if args.clean: 1014 if args.clean:
926 log('Cleaning {0}...'.format(outputDir)) 1015 log('Cleaning {0}...'.format(outputDir))
927 shutil.rmtree(outputDir) 1016 shutil.rmtree(outputDir)
934 mustBuild = args.force 1023 mustBuild = args.force
935 if not mustBuild: 1024 if not mustBuild:
936 for dep in p.all_deps([], False): 1025 for dep in p.all_deps([], False):
937 if dep.name in built: 1026 if dep.name in built:
938 mustBuild = True 1027 mustBuild = True
939 1028
1029 jasminAvailable = None
940 javafilelist = [] 1030 javafilelist = []
941 for sourceDir in sourceDirs: 1031 for sourceDir in sourceDirs:
942 for root, _, files in os.walk(sourceDir): 1032 for root, _, files in os.walk(sourceDir):
943 javafiles = [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java'] 1033 javafiles = [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java']
944 javafilelist += javafiles 1034 javafilelist += javafiles
945 1035
946 # Copy all non Java resources 1036 # Copy all non Java resources or assemble Jasmin files
947 nonjavafilelist = [join(root, name) for name in files if not name.endswith('.java')] 1037 nonjavafilelist = [join(root, name) for name in files if not name.endswith('.java')]
948 for src in nonjavafilelist: 1038 for src in nonjavafilelist:
949 dst = join(outputDir, src[len(sourceDir) + 1:]) 1039 if src.endswith('.jasm'):
950 if exists(dirname(dst)) and (not exists(dst) or os.path.getmtime(dst) != os.path.getmtime(src)): 1040 className = None
951 shutil.copyfile(src, dst) 1041 with open(src) as f:
952 1042 for line in f:
1043 if line.startswith('.class '):
1044 className = line.split()[-1]
1045 break
1046
1047 if className is not None:
1048 jasminOutputDir = p.jasmin_output_dir()
1049 classFile = join(jasminOutputDir, className.replace('/', os.sep) + '.class')
1050 if exists(dirname(classFile)) and (not exists(classFile) or os.path.getmtime(classFile) < os.path.getmtime(src)):
1051 if jasminAvailable is None:
1052 try:
1053 with open(os.devnull) as devnull:
1054 subprocess.call('jasmin', stdout=devnull, stderr=subprocess.STDOUT)
1055 jasminAvailable = True
1056 except OSError as e:
1057 jasminAvailable = False
1058
1059 if jasminAvailable:
1060 log('Assembling Jasmin file ' + src)
1061 run(['jasmin', '-d', jasminOutputDir, src])
1062 else:
1063 log('The jasmin executable could not be found - skipping ' + src)
1064 with file(classFile, 'a'):
1065 os.utime(classFile, None)
1066
1067 else:
1068 log('could not file .class directive in Jasmin source: ' + src)
1069 else:
1070 dst = join(outputDir, src[len(sourceDir) + 1:])
1071 if exists(dirname(dst)) and (not exists(dst) or os.path.getmtime(dst) != os.path.getmtime(src)):
1072 shutil.copyfile(src, dst)
1073
953 if not mustBuild: 1074 if not mustBuild:
954 for javafile in javafiles: 1075 for javafile in javafiles:
955 classfile = outputDir + javafile[len(sourceDir):-len('java')] + 'class' 1076 classfile = outputDir + javafile[len(sourceDir):-len('java')] + 'class'
956 if not exists(classfile) or os.path.getmtime(javafile) > os.path.getmtime(classfile): 1077 if not exists(classfile) or os.path.getmtime(javafile) > os.path.getmtime(classfile):
957 mustBuild = True 1078 mustBuild = True
958 break 1079 break
959 1080
960 if not mustBuild: 1081 if not mustBuild:
961 log('[all class files for {0} are up to date - skipping]'.format(p.name)) 1082 log('[all class files for {0} are up to date - skipping]'.format(p.name))
962 continue 1083 continue
963 1084
964 if len(javafilelist) == 0: 1085 if len(javafilelist) == 0:
965 log('[no Java sources for {0} - skipping]'.format(p.name)) 1086 log('[no Java sources for {0} - skipping]'.format(p.name))
966 continue 1087 continue
967 1088
968 built.add(p.name) 1089 built.add(p.name)
969 1090
970 argfileName = join(p.dir, 'javafilelist.txt') 1091 argfileName = join(p.dir, 'javafilelist.txt')
971 argfile = open(argfileName, 'wb') 1092 argfile = open(argfileName, 'wb')
972 argfile.write('\n'.join(javafilelist)) 1093 argfile.write('\n'.join(javafilelist))
973 argfile.close() 1094 argfile.close()
974 1095
975 try: 1096 try:
976 if jdtJar is None: 1097 if jdtJar is None:
977 log('Compiling Java sources for {0} with javac...'.format(p.name)) 1098 log('Compiling Java sources for {0} with javac...'.format(p.name))
978 errFilt = None 1099 errFilt = None
979 if not args.warnAPI: 1100 if not args.warnAPI:
980 class Filter: 1101 class Filter:
981 """ 1102 """
982 Class to errFilt the 'is Sun proprietary API and may be removed in a future release' 1103 Class to errFilt the 'is Sun proprietary API and may be removed in a future release'
983 warning when compiling the VM classes. 1104 warning when compiling the VM classes.
984 1105
985 """ 1106 """
986 def __init__(self): 1107 def __init__(self):
987 self.c = 0 1108 self.c = 0
988 1109
989 def eat(self, line): 1110 def eat(self, line):
990 if 'proprietary API' in line: 1111 if 'proprietary API' in line:
991 self.c = 2 1112 self.c = 2
992 elif self.c != 0: 1113 elif self.c != 0:
993 self.c -= 1 1114 self.c -= 1
994 else: 1115 else:
995 log(line.rstrip()) 1116 log(line.rstrip())
996 errFilt=Filter().eat 1117 errFilt=Filter().eat
997 1118
998 run([java().javac, '-g', '-J-Xmx1g', '-source', args.compliance, '-classpath', cp, '-d', outputDir, '@' + argfile.name], err=errFilt) 1119 run([java().javac, '-g', '-J-Xmx1g', '-source', args.compliance, '-classpath', cp, '-d', outputDir, '@' + argfile.name], err=errFilt)
999 else: 1120 else:
1000 log('Compiling Java sources for {0} with JDT...'.format(p.name)) 1121 log('Compiling Java sources for {0} with JDT...'.format(p.name))
1001 jdtProperties = join(p.dir, '.settings', 'org.eclipse.jdt.core.prefs') 1122 jdtProperties = join(p.dir, '.settings', 'org.eclipse.jdt.core.prefs')
1002 if not exists(jdtProperties): 1123 if not exists(jdtProperties):
1007 '-cp', cp, '-g', 1128 '-cp', cp, '-g',
1008 '-warn:-unusedImport,-unchecked', 1129 '-warn:-unusedImport,-unchecked',
1009 '-d', outputDir, '@' + argfile.name]) 1130 '-d', outputDir, '@' + argfile.name])
1010 finally: 1131 finally:
1011 os.remove(argfileName) 1132 os.remove(argfileName)
1012 1133
1013 if suppliedParser: 1134 if suppliedParser:
1014 return args 1135 return args
1015 return None 1136 return None
1016 1137
1017 def canonicalizeprojects(args): 1138 def canonicalizeprojects(args):
1018 """process all project files to canonicalize the dependencies 1139 """process all project files to canonicalize the dependencies
1019 1140
1020 The exit code of this command reflects how many files were updated.""" 1141 The exit code of this command reflects how many files were updated."""
1021 1142
1022 changedFiles = 0 1143 changedFiles = 0
1023 for s in suites(): 1144 for s in suites():
1024 projectsFile = join(s.dir, 'mx', 'projects') 1145 projectsFile = join(s.dir, 'mx', 'projects')
1025 if not exists(projectsFile): 1146 if not exists(projectsFile):
1026 continue 1147 continue
1037 out.write('project@' + m.group(1) + '@dependencies=' + ','.join(p.canonical_deps()) + '\n') 1158 out.write('project@' + m.group(1) + '@dependencies=' + ','.join(p.canonical_deps()) + '\n')
1038 content = out.getvalue() 1159 content = out.getvalue()
1039 if update_file(projectsFile, content): 1160 if update_file(projectsFile, content):
1040 changedFiles += 1 1161 changedFiles += 1
1041 return changedFiles; 1162 return changedFiles;
1042 1163
1043 def checkstyle(args): 1164 def checkstyle(args):
1044 """run Checkstyle on the Java sources 1165 """run Checkstyle on the Java sources
1045 1166
1046 Run Checkstyle over the Java sources. Any errors or warnings 1167 Run Checkstyle over the Java sources. Any errors or warnings
1047 produced by Checkstyle result in a non-zero exit code. 1168 produced by Checkstyle result in a non-zero exit code.
1048 1169
1049 If no projects are given, then all Java projects are checked.""" 1170 If no projects are given, then all Java projects are checked."""
1050 1171
1051 for p in sorted_deps(): 1172 for p in sorted_deps():
1052 if p.native: 1173 if p.native:
1053 continue 1174 continue
1054 sourceDirs = p.source_dirs() 1175 sourceDirs = p.source_dirs()
1055 dotCheckstyle = join(p.dir, '.checkstyle') 1176 dotCheckstyle = join(p.dir, '.checkstyle')
1056 1177
1057 if not exists(dotCheckstyle): 1178 if not exists(dotCheckstyle):
1058 continue 1179 continue
1059 1180
1060 for sourceDir in sourceDirs: 1181 for sourceDir in sourceDirs:
1061 javafilelist = [] 1182 javafilelist = []
1062 for root, _, files in os.walk(sourceDir): 1183 for root, _, files in os.walk(sourceDir):
1063 javafilelist += [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java'] 1184 javafilelist += [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java']
1064 if len(javafilelist) == 0: 1185 if len(javafilelist) == 0:
1073 if os.path.getmtime(f) > timestamp: 1194 if os.path.getmtime(f) > timestamp:
1074 mustCheck = True 1195 mustCheck = True
1075 break 1196 break
1076 else: 1197 else:
1077 mustCheck = True 1198 mustCheck = True
1078 1199
1079 if not mustCheck: 1200 if not mustCheck:
1080 log('[all Java sources in {0} already checked - skipping]'.format(sourceDir)) 1201 log('[all Java sources in {0} already checked - skipping]'.format(sourceDir))
1081 continue 1202 continue
1082 1203
1083 if exists(timestampFile): 1204 if exists(timestampFile):
1084 os.utime(timestampFile, None) 1205 os.utime(timestampFile, None)
1085 else: 1206 else:
1086 file(timestampFile, 'a') 1207 file(timestampFile, 'a')
1087 1208
1088 dotCheckstyleXML = xml.dom.minidom.parse(dotCheckstyle) 1209 dotCheckstyleXML = xml.dom.minidom.parse(dotCheckstyle)
1089 localCheckConfig = dotCheckstyleXML.getElementsByTagName('local-check-config')[0] 1210 localCheckConfig = dotCheckstyleXML.getElementsByTagName('local-check-config')[0]
1090 configLocation = localCheckConfig.getAttribute('location') 1211 configLocation = localCheckConfig.getAttribute('location')
1091 configType = localCheckConfig.getAttribute('type') 1212 configType = localCheckConfig.getAttribute('type')
1092 if configType == 'project': 1213 if configType == 'project':
1100 else: 1221 else:
1101 config = join(p.dir, configLocation) 1222 config = join(p.dir, configLocation)
1102 else: 1223 else:
1103 log('[unknown Checkstyle configuration type "' + configType + '" in {0} - skipping]'.format(sourceDir)) 1224 log('[unknown Checkstyle configuration type "' + configType + '" in {0} - skipping]'.format(sourceDir))
1104 continue 1225 continue
1105 1226
1106 exclude = join(p.dir, '.checkstyle.exclude') 1227 exclude = join(p.dir, '.checkstyle.exclude')
1107 1228
1108 if exists(exclude): 1229 if exists(exclude):
1109 with open(exclude) as f: 1230 with open(exclude) as f:
1110 # Convert patterns to OS separators 1231 # Convert patterns to OS separators
1111 patterns = [name.rstrip().replace('/', os.sep) for name in f.readlines()] 1232 patterns = [name.rstrip().replace('/', os.sep) for name in f.readlines()]
1112 def match(name): 1233 def match(name):
1113 for p in patterns: 1234 for p in patterns:
1114 if p in name: 1235 if p in name:
1115 log('excluding: ' + name) 1236 if _opts.verbose:
1237 log('excluding: ' + name)
1116 return True 1238 return True
1117 return False 1239 return False
1118 1240
1119 javafilelist = [name for name in javafilelist if not match(name)] 1241 javafilelist = [name for name in javafilelist if not match(name)]
1120 1242
1121 auditfileName = join(p.dir, 'checkstyleOutput.txt') 1243 auditfileName = join(p.dir, 'checkstyleOutput.txt')
1122 log('Running Checkstyle on {0} using {1}...'.format(sourceDir, config)) 1244 log('Running Checkstyle on {0} using {1}...'.format(sourceDir, config))
1123 1245
1124 try: 1246 try:
1125 1247
1126 # Checkstyle is unable to read the filenames to process from a file, and the 1248 # Checkstyle is unable to read the filenames to process from a file, and the
1127 # CreateProcess function on Windows limits the length of a command line to 1249 # CreateProcess function on Windows limits the length of a command line to
1128 # 32,768 characters (http://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx) 1250 # 32,768 characters (http://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx)
1135 if (size + s < 30000): 1257 if (size + s < 30000):
1136 size += s 1258 size += s
1137 i += 1 1259 i += 1
1138 else: 1260 else:
1139 break 1261 break
1140 1262
1141 batch = javafilelist[:i] 1263 batch = javafilelist[:i]
1142 javafilelist = javafilelist[i:] 1264 javafilelist = javafilelist[i:]
1143 try: 1265 try:
1144 run_java(['-Xmx1g', '-jar', library('CHECKSTYLE').get_path(True), '-c', config, '-o', auditfileName] + batch) 1266 run_java(['-Xmx1g', '-jar', library('CHECKSTYLE').get_path(True), '-c', config, '-o', auditfileName] + batch)
1145 finally: 1267 finally:
1160 Removes all files created by a build, including Java class files, executables, and 1282 Removes all files created by a build, including Java class files, executables, and
1161 generated images. 1283 generated images.
1162 """ 1284 """
1163 1285
1164 suppliedParser = parser is not None 1286 suppliedParser = parser is not None
1165 1287
1166 parser = parser if suppliedParser else ArgumentParser(prog='mx build'); 1288 parser = parser if suppliedParser else ArgumentParser(prog='mx build');
1167 parser.add_argument('--no-native', action='store_false', dest='native', help='do not clean native projects') 1289 parser.add_argument('--no-native', action='store_false', dest='native', help='do not clean native projects')
1168 parser.add_argument('--no-java', action='store_false', dest='java', help='do not clean Java projects') 1290 parser.add_argument('--no-java', action='store_false', dest='java', help='do not clean Java projects')
1169 1291
1170 args = parser.parse_args(args) 1292 args = parser.parse_args(args)
1171 1293
1172 for p in projects(): 1294 for p in projects():
1173 if p.native: 1295 if p.native:
1174 if args.native: 1296 if args.native:
1175 run([gmake_cmd(), '-C', p.dir, 'clean']) 1297 run([gmake_cmd(), '-C', p.dir, 'clean'])
1176 else: 1298 else:
1177 if args.java: 1299 if args.java:
1178 outputDir = p.output_dir() 1300 outputDir = p.output_dir()
1179 if outputDir != '' and exists(outputDir): 1301 if outputDir != '' and exists(outputDir):
1180 log('Removing {0}...'.format(outputDir)) 1302 log('Removing {0}...'.format(outputDir))
1181 shutil.rmtree(outputDir) 1303 shutil.rmtree(outputDir)
1182 1304
1183 if suppliedParser: 1305 if suppliedParser:
1184 return args 1306 return args
1185 1307
1308 def about(args):
1309 """show the 'man page' for mx"""
1310 print __doc__
1311
1186 def help_(args): 1312 def help_(args):
1187 """show help for a given command 1313 """show help for a given command
1188 1314
1189 With no arguments, print a list of commands and short help for each command. 1315 With no arguments, print a list of commands and short help for each command.
1190 1316
1191 Given a command name, print help for that command.""" 1317 Given a command name, print help for that command."""
1192 if len(args) == 0: 1318 if len(args) == 0:
1193 _argParser.print_help() 1319 _argParser.print_help()
1194 return 1320 return
1195 1321
1196 name = args[0] 1322 name = args[0]
1197 if not commands.has_key(name): 1323 if not commands.has_key(name):
1198 _argParser.error('unknown command: ' + name) 1324 _argParser.error('unknown command: ' + name)
1199 1325
1200 value = commands[name] 1326 value = commands[name]
1201 (func, usage) = value[:2] 1327 (func, usage) = value[:2]
1202 doc = func.__doc__ 1328 doc = func.__doc__
1203 if len(value) > 2: 1329 if len(value) > 2:
1204 docArgs = value[2:] 1330 docArgs = value[2:]
1211 doc = doc.format(*fmtArgs) 1337 doc = doc.format(*fmtArgs)
1212 print 'mx {0} {1}\n\n{2}\n'.format(name, usage, doc) 1338 print 'mx {0} {1}\n\n{2}\n'.format(name, usage, doc)
1213 1339
1214 def projectgraph(args, suite=None): 1340 def projectgraph(args, suite=None):
1215 """create dot graph for project structure ("mx projectgraph | dot -Tpdf -oprojects.pdf")""" 1341 """create dot graph for project structure ("mx projectgraph | dot -Tpdf -oprojects.pdf")"""
1216 1342
1217 print 'digraph projects {' 1343 print 'digraph projects {'
1218 print 'rankdir=BT;' 1344 print 'rankdir=BT;'
1219 print 'node [shape=rect];' 1345 print 'node [shape=rect];'
1220 for p in projects(): 1346 for p in projects():
1221 for dep in p.canonical_deps(): 1347 for dep in p.canonical_deps():
1225 def eclipseinit(args, suite=None): 1351 def eclipseinit(args, suite=None):
1226 """(re)generate Eclipse project configurations""" 1352 """(re)generate Eclipse project configurations"""
1227 1353
1228 if suite is None: 1354 if suite is None:
1229 suite = _mainSuite 1355 suite = _mainSuite
1230 1356
1231 def println(out, obj): 1357 def println(out, obj):
1232 out.write(str(obj) + '\n') 1358 out.write(str(obj) + '\n')
1233 1359
1234 for p in projects(): 1360 for p in projects():
1235 if p.native:
1236 continue
1237
1238 if not exists(p.dir): 1361 if not exists(p.dir):
1239 os.makedirs(p.dir) 1362 os.makedirs(p.dir)
1240 1363
1364 if p.native:
1365 eclipseNativeSettingsDir = join(suite.dir, 'mx', 'eclipse-native-settings')
1366 if exists(eclipseNativeSettingsDir):
1367 for name in os.listdir(eclipseNativeSettingsDir):
1368 path = join(eclipseNativeSettingsDir, name)
1369 if isfile(path):
1370 with open(join(eclipseNativeSettingsDir, name)) as f:
1371 content = f.read()
1372 content = content.replace('${javaHome}', java().jdk)
1373 update_file(join(p.dir, name), content)
1374 continue
1375
1241 out = StringIO.StringIO() 1376 out = StringIO.StringIO()
1242 1377
1243 println(out, '<?xml version="1.0" encoding="UTF-8"?>') 1378 println(out, '<?xml version="1.0" encoding="UTF-8"?>')
1244 println(out, '<classpath>') 1379 println(out, '<classpath>')
1245 for src in p.srcDirs: 1380 for src in p.srcDirs:
1246 srcDir = join(p.dir, src) 1381 srcDir = join(p.dir, src)
1247 if not exists(srcDir): 1382 if not exists(srcDir):
1248 os.mkdir(srcDir) 1383 os.mkdir(srcDir)
1249 println(out, '\t<classpathentry kind="src" path="' + src + '"/>') 1384 println(out, '\t<classpathentry kind="src" path="' + src + '"/>')
1250 1385
1251 # Every Java program depends on the JRE 1386 # Every Java program depends on the JRE
1252 println(out, '\t<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>') 1387 println(out, '\t<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>')
1253 1388
1254 for dep in p.all_deps([], True): 1389 for dep in p.all_deps([], True):
1255 if dep == p: 1390 if dep == p:
1256 continue; 1391 continue;
1257 1392
1258 if dep.isLibrary(): 1393 if dep.isLibrary():
1259 if hasattr(dep, 'eclipse.container'): 1394 if hasattr(dep, 'eclipse.container'):
1260 println(out, '\t<classpathentry exported="true" kind="con" path="' + getattr(dep, 'eclipse.container') + '"/>') 1395 println(out, '\t<classpathentry exported="true" kind="con" path="' + getattr(dep, 'eclipse.container') + '"/>')
1261 elif hasattr(dep, 'eclipse.project'): 1396 elif hasattr(dep, 'eclipse.project'):
1262 println(out, '\t<classpathentry combineaccessrules="false" exported="true" kind="src" path="/' + getattr(dep, 'eclipse.project') + '"/>') 1397 println(out, '\t<classpathentry combineaccessrules="false" exported="true" kind="src" path="/' + getattr(dep, 'eclipse.project') + '"/>')
1265 if dep.mustExist: 1400 if dep.mustExist:
1266 dep.get_path(resolve=True) 1401 dep.get_path(resolve=True)
1267 if isabs(path): 1402 if isabs(path):
1268 println(out, '\t<classpathentry exported="true" kind="lib" path="' + path + '"/>') 1403 println(out, '\t<classpathentry exported="true" kind="lib" path="' + path + '"/>')
1269 else: 1404 else:
1270 println(out, '\t<classpathentry exported="true" kind="lib" path="' + join(suite.dir, path) + '"/>') 1405 projRelPath = os.path.relpath(join(suite.dir, path), p.dir)
1406 println(out, '\t<classpathentry exported="true" kind="lib" path="' + projRelPath + '"/>')
1271 else: 1407 else:
1272 println(out, '\t<classpathentry combineaccessrules="false" exported="true" kind="src" path="/' + dep.name + '"/>') 1408 println(out, '\t<classpathentry combineaccessrules="false" exported="true" kind="src" path="/' + dep.name + '"/>')
1273 1409
1274 println(out, '\t<classpathentry kind="output" path="' + getattr(p, 'eclipse.output', 'bin') + '"/>') 1410 println(out, '\t<classpathentry kind="output" path="' + getattr(p, 'eclipse.output', 'bin') + '"/>')
1275 println(out, '</classpath>') 1411 println(out, '</classpath>')
1276 update_file(join(p.dir, '.classpath'), out.getvalue()) 1412 update_file(join(p.dir, '.classpath'), out.getvalue())
1277 out.close() 1413 out.close()
1278 1414
1279 csConfig = join(project(p.checkstyleProj).dir, '.checkstyle_checks.xml') 1415 csConfig = join(project(p.checkstyleProj).dir, '.checkstyle_checks.xml')
1280 if exists(csConfig): 1416 if exists(csConfig):
1281 out = StringIO.StringIO() 1417 out = StringIO.StringIO()
1282 1418
1283 dotCheckstyle = join(p.dir, ".checkstyle") 1419 dotCheckstyle = join(p.dir, ".checkstyle")
1284 checkstyleConfigPath = '/' + p.checkstyleProj + '/.checkstyle_checks.xml' 1420 checkstyleConfigPath = '/' + p.checkstyleProj + '/.checkstyle_checks.xml'
1285 println(out, '<?xml version="1.0" encoding="UTF-8"?>') 1421 println(out, '<?xml version="1.0" encoding="UTF-8"?>')
1286 println(out, '<fileset-config file-format-version="1.2.0" simple-config="true">') 1422 println(out, '<fileset-config file-format-version="1.2.0" simple-config="true">')
1287 println(out, '\t<local-check-config name="Checks" location="' + checkstyleConfigPath + '" type="project" description="">') 1423 println(out, '\t<local-check-config name="Checks" location="' + checkstyleConfigPath + '" type="project" description="">')
1303 line = line.strip() 1439 line = line.strip()
1304 exclDir = join(p.dir, line) 1440 exclDir = join(p.dir, line)
1305 assert isdir(exclDir), 'excluded source directory listed in ' + exclude + ' does not exist or is not a directory: ' + exclDir 1441 assert isdir(exclDir), 'excluded source directory listed in ' + exclude + ' does not exist or is not a directory: ' + exclDir
1306 println(out, '\t\t<filter-data value="' + line + '"/>') 1442 println(out, '\t\t<filter-data value="' + line + '"/>')
1307 println(out, '\t</filter>') 1443 println(out, '\t</filter>')
1308 1444
1309 println(out, '</fileset-config>') 1445 println(out, '</fileset-config>')
1310 update_file(dotCheckstyle, out.getvalue()) 1446 update_file(dotCheckstyle, out.getvalue())
1311 out.close() 1447 out.close()
1312 1448
1313 1449
1314 out = StringIO.StringIO() 1450 out = StringIO.StringIO()
1315 1451
1316 println(out, '<?xml version="1.0" encoding="UTF-8"?>') 1452 println(out, '<?xml version="1.0" encoding="UTF-8"?>')
1317 println(out, '<projectDescription>') 1453 println(out, '<projectDescription>')
1318 println(out, '\t<name>' + p.name + '</name>') 1454 println(out, '\t<name>' + p.name + '</name>')
1319 println(out, '\t<comment></comment>') 1455 println(out, '\t<comment></comment>')
1320 println(out, '\t<projects>') 1456 println(out, '\t<projects>')
1351 for name in os.listdir(eclipseSettingsDir): 1487 for name in os.listdir(eclipseSettingsDir):
1352 path = join(eclipseSettingsDir, name) 1488 path = join(eclipseSettingsDir, name)
1353 if isfile(path): 1489 if isfile(path):
1354 with open(join(eclipseSettingsDir, name)) as f: 1490 with open(join(eclipseSettingsDir, name)) as f:
1355 content = f.read() 1491 content = f.read()
1492 content = content.replace('${javaCompliance}', str(p.javaCompliance))
1356 update_file(join(settingsDir, name), content) 1493 update_file(join(settingsDir, name), content)
1357 1494
1358 def netbeansinit(args, suite=None): 1495 def netbeansinit(args, suite=None):
1359 """(re)generate NetBeans project configurations""" 1496 """(re)generate NetBeans project configurations"""
1360 1497
1361 if suite is None: 1498 if suite is None:
1362 suite = _mainSuite 1499 suite = _mainSuite
1363 1500
1364 def println(out, obj): 1501 def println(out, obj):
1365 out.write(str(obj) + '\n') 1502 out.write(str(obj) + '\n')
1366 1503
1367 updated = False 1504 updated = False
1368 for p in projects(): 1505 for p in projects():
1369 if p.native: 1506 if p.native:
1370 continue 1507 continue
1371 1508
1372 if not exists(join(p.dir, 'nbproject')): 1509 if not exists(join(p.dir, 'nbproject')):
1373 os.makedirs(join(p.dir, 'nbproject')) 1510 os.makedirs(join(p.dir, 'nbproject'))
1374 1511
1375 out = StringIO.StringIO() 1512 out = StringIO.StringIO()
1376 1513
1377 println(out, '<?xml version="1.0" encoding="UTF-8"?>') 1514 println(out, '<?xml version="1.0" encoding="UTF-8"?>')
1378 println(out, '<project name="' + p.name + '" default="default" basedir=".">') 1515 println(out, '<project name="' + p.name + '" default="default" basedir=".">')
1379 println(out, '\t<description>Builds, tests, and runs the project ' + p.name + '.</description>') 1516 println(out, '\t<description>Builds, tests, and runs the project ' + p.name + '.</description>')
1380 println(out, '\t<import file="nbproject/build-impl.xml"/>') 1517 println(out, '\t<import file="nbproject/build-impl.xml"/>')
1381 println(out, '</project>') 1518 println(out, '</project>')
1382 updated = update_file(join(p.dir, 'build.xml'), out.getvalue()) or updated 1519 updated = update_file(join(p.dir, 'build.xml'), out.getvalue()) or updated
1383 out.close() 1520 out.close()
1384 1521
1385 out = StringIO.StringIO() 1522 out = StringIO.StringIO()
1386 println(out, '<?xml version="1.0" encoding="UTF-8"?>') 1523 println(out, '<?xml version="1.0" encoding="UTF-8"?>')
1387 println(out, '<project xmlns="http://www.netbeans.org/ns/project/1">') 1524 println(out, '<project xmlns="http://www.netbeans.org/ns/project/1">')
1388 println(out, ' <type>org.netbeans.modules.java.j2seproject</type>') 1525 println(out, ' <type>org.netbeans.modules.java.j2seproject</type>')
1389 println(out, ' <configuration>') 1526 println(out, ' <configuration>')
1395 println(out, ' </source-roots>') 1532 println(out, ' </source-roots>')
1396 println(out, ' <test-roots>') 1533 println(out, ' <test-roots>')
1397 println(out, ' <root id="test.src.dir"/>') 1534 println(out, ' <root id="test.src.dir"/>')
1398 println(out, ' </test-roots>') 1535 println(out, ' </test-roots>')
1399 println(out, ' </data>') 1536 println(out, ' </data>')
1400 1537
1401 firstDep = True 1538 firstDep = True
1402 for dep in p.all_deps([], True): 1539 for dep in p.all_deps([], True):
1403 if dep == p: 1540 if dep == p:
1404 continue; 1541 continue;
1405 1542
1406 if not dep.isLibrary(): 1543 if not dep.isLibrary():
1407 n = dep.name.replace('.', '_') 1544 n = dep.name.replace('.', '_')
1408 if firstDep: 1545 if firstDep:
1409 println(out, ' <references xmlns="http://www.netbeans.org/ns/ant-project-references/1">') 1546 println(out, ' <references xmlns="http://www.netbeans.org/ns/ant-project-references/1">')
1410 firstDep = False 1547 firstDep = False
1411 1548
1412 println(out, ' <reference>') 1549 println(out, ' <reference>')
1413 println(out, ' <foreign-project>' + n + '</foreign-project>') 1550 println(out, ' <foreign-project>' + n + '</foreign-project>')
1414 println(out, ' <artifact-type>jar</artifact-type>') 1551 println(out, ' <artifact-type>jar</artifact-type>')
1415 println(out, ' <script>build.xml</script>') 1552 println(out, ' <script>build.xml</script>')
1416 println(out, ' <target>jar</target>') 1553 println(out, ' <target>jar</target>')
1417 println(out, ' <clean-target>clean</clean-target>') 1554 println(out, ' <clean-target>clean</clean-target>')
1418 println(out, ' <id>jar</id>') 1555 println(out, ' <id>jar</id>')
1419 println(out, ' </reference>') 1556 println(out, ' </reference>')
1420 1557
1421 if not firstDep: 1558 if not firstDep:
1422 println(out, ' </references>') 1559 println(out, ' </references>')
1423 1560
1424 println(out, ' </configuration>') 1561 println(out, ' </configuration>')
1425 println(out, '</project>') 1562 println(out, '</project>')
1426 updated = update_file(join(p.dir, 'nbproject', 'project.xml'), out.getvalue()) or updated 1563 updated = update_file(join(p.dir, 'nbproject', 'project.xml'), out.getvalue()) or updated
1427 out.close() 1564 out.close()
1428 1565
1429 out = StringIO.StringIO() 1566 out = StringIO.StringIO()
1430 1567
1431 jdkPlatform = 'JDK_' + java().version 1568 jdkPlatform = 'JDK_' + java().version
1432 1569
1433 content = """ 1570 content = """
1434 annotation.processing.enabled=false 1571 annotation.processing.enabled=false
1435 annotation.processing.enabled.in.editor=false 1572 annotation.processing.enabled.in.editor=false
1436 annotation.processing.processors.list= 1573 annotation.processing.processors.list=
1437 annotation.processing.run.all.processors=true 1574 annotation.processing.run.all.processors=true
1515 if mainSrc: 1652 if mainSrc:
1516 println(out, 'src.dir=${' + ref + '}') 1653 println(out, 'src.dir=${' + ref + '}')
1517 mainSrc = False 1654 mainSrc = False
1518 else: 1655 else:
1519 println(out, 'src.' + src + '.dir=${' + ref + '}') 1656 println(out, 'src.' + src + '.dir=${' + ref + '}')
1520 1657
1521 javacClasspath = [] 1658 javacClasspath = []
1522 for dep in p.all_deps([], True): 1659 for dep in p.all_deps([], True):
1523 if dep == p: 1660 if dep == p:
1524 continue; 1661 continue;
1525 1662
1526 if dep.isLibrary(): 1663 if dep.isLibrary():
1527 if not dep.mustExist: 1664 if not dep.mustExist:
1528 continue 1665 continue
1529 path = dep.get_path(resolve=True) 1666 path = dep.get_path(resolve=True)
1530 if os.sep == '\\': 1667 if os.sep == '\\':
1531 path = path.replace('\\', '\\\\') 1668 path = path.replace('\\', '\\\\')
1532 ref = 'file.reference.' + dep.name + '-bin' 1669 ref = 'file.reference.' + dep.name + '-bin'
1533 println(out, ref + '=' + path) 1670 println(out, ref + '=' + path)
1534 1671
1535 else: 1672 else:
1536 n = dep.name.replace('.', '_') 1673 n = dep.name.replace('.', '_')
1537 relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/') 1674 relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/')
1538 ref = 'reference.' + n + '.jar' 1675 ref = 'reference.' + n + '.jar'
1539 println(out, 'project.' + n + '=' + relDepPath) 1676 println(out, 'project.' + n + '=' + relDepPath)
1540 println(out, ref + '=${project.' + n + '}/dist/' + dep.name + '.jar') 1677 println(out, ref + '=${project.' + n + '}/dist/' + dep.name + '.jar')
1541 1678
1542 javacClasspath.append('${' + ref + '}') 1679 javacClasspath.append('${' + ref + '}')
1543 1680
1544 println(out, 'javac.classpath=\\\n ' + (os.pathsep + '\\\n ').join(javacClasspath)) 1681 println(out, 'javac.classpath=\\\n ' + (os.pathsep + '\\\n ').join(javacClasspath))
1545 1682
1546 1683
1547 updated = update_file(join(p.dir, 'nbproject', 'project.properties'), out.getvalue()) or updated 1684 updated = update_file(join(p.dir, 'nbproject', 'project.properties'), out.getvalue()) or updated
1548 out.close() 1685 out.close()
1549 1686
1550 if updated: 1687 if updated:
1551 log('If using NetBeans:') 1688 log('If using NetBeans:')
1552 log(' 1. Ensure that a platform named "JDK ' + java().version + '" is defined (Tools -> Java Platforms)') 1689 log(' 1. Ensure that a platform named "JDK ' + java().version + '" is defined (Tools -> Java Platforms)')
1553 log(' 2. Open/create a Project Group for the directory containing the projects (File -> Project Group -> New Group... -> Folder of Projects)') 1690 log(' 2. Open/create a Project Group for the directory containing the projects (File -> Project Group -> New Group... -> Folder of Projects)')
1554 1691
1555 def ideclean(args, suite=None): 1692 def ideclean(args, suite=None):
1556 """remove all Eclipse and NetBeans project configurations""" 1693 """remove all Eclipse and NetBeans project configurations"""
1557 1694
1558 def rm(path): 1695 def rm(path):
1559 if exists(path): 1696 if exists(path):
1560 os.remove(path) 1697 os.remove(path)
1561 1698
1562 for p in projects(): 1699 for p in projects():
1563 if p.native: 1700 if p.native:
1564 continue 1701 continue
1565 1702
1566 shutil.rmtree(join(p.dir, '.settings'), ignore_errors=True) 1703 shutil.rmtree(join(p.dir, '.settings'), ignore_errors=True)
1567 shutil.rmtree(join(p.dir, 'nbproject'), ignore_errors=True) 1704 shutil.rmtree(join(p.dir, 'nbproject'), ignore_errors=True)
1568 rm(join(p.dir, '.classpath')) 1705 rm(join(p.dir, '.classpath'))
1569 rm(join(p.dir, '.project')) 1706 rm(join(p.dir, '.project'))
1570 rm(join(p.dir, 'build.xml')) 1707 rm(join(p.dir, 'build.xml'))
1571 1708
1572 def ideinit(args, suite=None): 1709 def ideinit(args, suite=None):
1573 """(re)generate Eclipse and NetBeans project configurations""" 1710 """(re)generate Eclipse and NetBeans project configurations"""
1574 eclipseinit(args, suite) 1711 eclipseinit(args, suite)
1575 netbeansinit(args, suite) 1712 netbeansinit(args, suite)
1576 1713
1578 """launch javap with a -classpath option denoting all available classes 1715 """launch javap with a -classpath option denoting all available classes
1579 1716
1580 Run the JDK javap class file disassembler with the following prepended options: 1717 Run the JDK javap class file disassembler with the following prepended options:
1581 1718
1582 -private -verbose -classpath <path to project classes>""" 1719 -private -verbose -classpath <path to project classes>"""
1583 1720
1584 javap = java().javap 1721 javap = java().javap
1585 if not exists(javap): 1722 if not exists(javap):
1586 abort('The javap executable does not exists: ' + javap) 1723 abort('The javap executable does not exists: ' + javap)
1587 else: 1724 else:
1588 run([javap, '-private', '-verbose', '-classpath', classpath()] + args) 1725 run([javap, '-private', '-verbose', '-classpath', classpath()] + args)
1600 """ 1737 """
1601 Define how a single command-line argument. 1738 Define how a single command-line argument.
1602 """ 1739 """
1603 assert _argParser is not None 1740 assert _argParser is not None
1604 _argParser.add_argument(*args, **kwargs) 1741 _argParser.add_argument(*args, **kwargs)
1605 1742
1606 # Table of commands in alphabetical order. 1743 # Table of commands in alphabetical order.
1607 # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...] 1744 # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...]
1608 # If any of the format args are instances of Callable, then they are called with an 'env' are before being 1745 # If any of the format args are instances of Callable, then they are called with an 'env' are before being
1609 # used in the call to str.format(). 1746 # used in the call to str.format().
1610 # Extensions should update this table directly 1747 # Extensions should update this table directly
1611 commands = { 1748 commands = {
1749 'about': [about, ''],
1612 'build': [build, '[options]'], 1750 'build': [build, '[options]'],
1613 'checkstyle': [checkstyle, ''], 1751 'checkstyle': [checkstyle, ''],
1614 'canonicalizeprojects': [canonicalizeprojects, ''], 1752 'canonicalizeprojects': [canonicalizeprojects, ''],
1615 'clean': [clean, ''], 1753 'clean': [clean, ''],
1616 'eclipseinit': [eclipseinit, ''], 1754 'eclipseinit': [eclipseinit, ''],
1628 def main(): 1766 def main():
1629 cwdMxDir = join(os.getcwd(), 'mx') 1767 cwdMxDir = join(os.getcwd(), 'mx')
1630 if exists(cwdMxDir) and isdir(cwdMxDir): 1768 if exists(cwdMxDir) and isdir(cwdMxDir):
1631 global _mainSuite 1769 global _mainSuite
1632 _mainSuite = _loadSuite(os.getcwd(), True) 1770 _mainSuite = _loadSuite(os.getcwd(), True)
1633 1771
1634 opts, commandAndArgs = _argParser._parse_cmd_line() 1772 opts, commandAndArgs = _argParser._parse_cmd_line()
1635 1773
1636 global _opts, _java 1774 global _opts, _java
1637 _opts = opts 1775 _opts = opts
1638 _java = JavaConfig(opts) 1776 _java = JavaConfig(opts)
1639 1777
1640 for s in suites(): 1778 for s in suites():
1641 s._post_init(opts) 1779 s._post_init(opts)
1642 1780
1643 if len(commandAndArgs) == 0: 1781 if len(commandAndArgs) == 0:
1644 _argParser.print_help() 1782 _argParser.print_help()
1645 return 1783 return
1646 1784
1647 command = commandAndArgs[0] 1785 command = commandAndArgs[0]
1648 command_args = commandAndArgs[1:] 1786 command_args = commandAndArgs[1:]
1649 1787
1650 if not commands.has_key(command): 1788 if not commands.has_key(command):
1651 abort('mx: unknown command \'{0}\'\n{1}use "mx help" for more options'.format(command, _format_commands())) 1789 abort('mx: unknown command \'{0}\'\n{1}use "mx help" for more options'.format(command, _format_commands()))
1652 1790
1653 c, _ = commands[command][:2] 1791 c, _ = commands[command][:2]
1654 def term_handler(signum, frame): 1792 def term_handler(signum, frame):
1655 abort(1) 1793 abort(1)
1656 signal.signal(signal.SIGTERM, term_handler) 1794 signal.signal(signal.SIGTERM, term_handler)
1657 try: 1795 try: