blob: 9c5c2ec78fa97ca2b5172e23b4e48f8e92d76151 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.bnd.build;
2
3import java.io.*;
4import java.lang.reflect.*;
5import java.net.*;
6import java.util.*;
7import java.util.Map.Entry;
Stuart McCullochbb014372012-06-07 21:57:32 +00008import java.util.concurrent.locks.*;
9import java.util.jar.*;
10
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +000011import aQute.bnd.header.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000012import aQute.bnd.help.*;
13import aQute.bnd.maven.support.*;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +000014import aQute.bnd.osgi.*;
15import aQute.bnd.osgi.eclipse.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000016import aQute.bnd.service.*;
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +000017import aQute.bnd.service.RepositoryPlugin.PutResult;
Stuart McCullochbb014372012-06-07 21:57:32 +000018import aQute.bnd.service.RepositoryPlugin.Strategy;
19import aQute.bnd.service.action.*;
Stuart McCulloch6a046662012-07-19 13:11:20 +000020import aQute.bnd.version.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000021import aQute.lib.io.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000022import aQute.libg.generics.*;
Stuart McCulloch2286f232012-06-15 13:27:53 +000023import aQute.libg.reporter.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000024import aQute.libg.sed.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000025
26/**
27 * This class is NOT threadsafe
Stuart McCullochbb014372012-06-07 21:57:32 +000028 */
29
30public class Project extends Processor {
31
32 final static String DEFAULT_ACTIONS = "build; label='Build', test; label='Test', run; label='Run', clean; label='Clean', release; label='Release', refreshAll; label=Refresh, deploy;label=Deploy";
33 public final static String BNDFILE = "bnd.bnd";
34 public final static String BNDCNF = "cnf";
35 final Workspace workspace;
36 boolean preparedPaths;
37 final Collection<Project> dependson = new LinkedHashSet<Project>();
38 final Collection<Container> classpath = new LinkedHashSet<Container>();
39 final Collection<Container> buildpath = new LinkedHashSet<Container>();
40 final Collection<Container> testpath = new LinkedHashSet<Container>();
41 final Collection<Container> runpath = new LinkedHashSet<Container>();
42 final Collection<Container> runbundles = new LinkedHashSet<Container>();
43 File runstorage;
44 final Collection<File> sourcepath = new LinkedHashSet<File>();
45 final Collection<File> allsourcepath = new LinkedHashSet<File>();
46 final Collection<Container> bootclasspath = new LinkedHashSet<Container>();
47 final Lock lock = new ReentrantLock(true);
48 volatile String lockingReason;
49 volatile Thread lockingThread;
50 File output;
51 File target;
52 boolean inPrepare;
53 int revision;
54 File files[];
55 static List<Project> trail = new ArrayList<Project>();
56 boolean delayRunDependencies = false;
Stuart McCulloch2286f232012-06-15 13:27:53 +000057 final ProjectMessages msgs = ReporterMessages.base(this, ProjectMessages.class);
Stuart McCullochbb014372012-06-07 21:57:32 +000058
Stuart McCullochd4826102012-06-26 16:34:24 +000059 public Project(Workspace workspace, @SuppressWarnings("unused") File projectDir, File buildFile) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +000060 super(workspace);
61 this.workspace = workspace;
62 setFileMustExist(false);
63 setProperties(buildFile);
64 assert workspace != null;
65 // For backward compatibility reasons, we also read
66 readBuildProperties();
67 }
68
69 public Project(Workspace workspace, File buildDir) throws Exception {
70 this(workspace, buildDir, new File(buildDir, BNDFILE));
71 }
72
73 private void readBuildProperties() throws Exception {
74 try {
75 File f = getFile("build.properties");
76 if (f.isFile()) {
77 Properties p = loadProperties(f);
Stuart McCulloch2286f232012-06-15 13:27:53 +000078 for (Enumeration< ? > e = p.propertyNames(); e.hasMoreElements();) {
Stuart McCullochbb014372012-06-07 21:57:32 +000079 String key = (String) e.nextElement();
80 String newkey = key;
81 if (key.indexOf('$') >= 0) {
82 newkey = getReplacer().process(key);
83 }
84 setProperty(newkey, p.getProperty(key));
85 }
86 }
Stuart McCulloch2286f232012-06-15 13:27:53 +000087 }
88 catch (Exception e) {
Stuart McCullochbb014372012-06-07 21:57:32 +000089 e.printStackTrace();
90 }
91 }
92
93 public static Project getUnparented(File propertiesFile) throws Exception {
94 propertiesFile = propertiesFile.getAbsoluteFile();
95 Workspace workspace = new Workspace(propertiesFile.getParentFile());
96 Project project = new Project(workspace, propertiesFile.getParentFile());
97 project.setProperties(propertiesFile);
98 project.setFileMustExist(true);
99 return project;
100 }
101
102 public synchronized boolean isValid() {
103 return getBase().isDirectory() && getPropertiesFile().isFile();
104 }
105
106 /**
107 * Return a new builder that is nicely setup for this project. Please close
108 * this builder after use.
109 *
110 * @param parent
111 * The project builder to use as parent, use this project if null
112 * @return
113 * @throws Exception
114 */
115 public synchronized ProjectBuilder getBuilder(ProjectBuilder parent) throws Exception {
116
117 ProjectBuilder builder;
118
119 if (parent == null)
120 builder = new ProjectBuilder(this);
121 else
122 builder = new ProjectBuilder(parent);
123
124 builder.setBase(getBase());
Stuart McCullochd4826102012-06-26 16:34:24 +0000125 builder.setPedantic(isPedantic());
126 builder.setTrace(isTrace());
Stuart McCullochbb014372012-06-07 21:57:32 +0000127 return builder;
128 }
129
130 public synchronized int getChanged() {
131 return revision;
132 }
133
134 /*
135 * Indicate a change in the external world that affects our build. This will
136 * clear any cached results.
137 */
138 public synchronized void setChanged() {
139 // if (refresh()) {
140 preparedPaths = false;
141 files = null;
142 revision++;
143 // }
144 }
145
146 public Workspace getWorkspace() {
147 return workspace;
148 }
149
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000150 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +0000151 public String toString() {
152 return getBase().getName();
153 }
154
155 /**
156 * Set up all the paths
157 */
158
159 public synchronized void prepare() throws Exception {
160 if (!isValid()) {
161 warning("Invalid project attempts to prepare: %s", this);
162 return;
163 }
164
165 if (inPrepare)
166 throw new CircularDependencyException(trail.toString() + "," + this);
167
168 trail.add(this);
169 try {
170 if (!preparedPaths) {
171 inPrepare = true;
172 try {
173 dependson.clear();
174 buildpath.clear();
175 sourcepath.clear();
176 allsourcepath.clear();
177 bootclasspath.clear();
178 testpath.clear();
179 runpath.clear();
180 runbundles.clear();
181
182 // We use a builder to construct all the properties for
183 // use.
184 setProperty("basedir", getBase().getAbsolutePath());
185
186 // If a bnd.bnd file exists, we read it.
187 // Otherwise, we just do the build properties.
188 if (!getPropertiesFile().isFile() && new File(getBase(), ".classpath").isFile()) {
189 // Get our Eclipse info, we might depend on other
190 // projects
191 // though ideally this should become empty and void
192 doEclipseClasspath();
193 }
194
195 // Calculate our source directory
196
197 File src = getSrc();
198 if (src.isDirectory()) {
199 sourcepath.add(src);
200 allsourcepath.add(src);
201 } else
202 sourcepath.add(getBase());
203
204 // Set default bin directory
Stuart McCullochffa8aaf2012-06-17 20:38:35 +0000205 output = getOutput0();
Stuart McCullochbb014372012-06-07 21:57:32 +0000206 if (!output.exists()) {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000207 if (!output.mkdirs()) {
208 throw new IOException("Could not create directory " + output);
209 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000210 getWorkspace().changedFile(output);
211 }
212 if (!output.isDirectory())
Stuart McCulloch2286f232012-06-15 13:27:53 +0000213 msgs.NoOutputDirectory_(output);
Stuart McCullochbb014372012-06-07 21:57:32 +0000214 else {
215 Container c = new Container(this, output);
216 if (!buildpath.contains(c))
217 buildpath.add(c);
218 }
219
220 // Where we store all our generated stuff.
Stuart McCullochffa8aaf2012-06-17 20:38:35 +0000221 target = getTarget0();
Stuart McCullochbb014372012-06-07 21:57:32 +0000222
223 // Where the launched OSGi framework stores stuff
224 String runStorageStr = getProperty(Constants.RUNSTORAGE);
225 runstorage = runStorageStr != null ? getFile(runStorageStr) : null;
226
227 // We might have some other projects we want build
228 // before we do anything, but these projects are not in
229 // our path. The -dependson allows you to build them before.
230
231 List<Project> dependencies = new ArrayList<Project>();
232 // dependencies.add( getWorkspace().getProject("cnf"));
233
234 String dp = getProperty(Constants.DEPENDSON);
235 Set<String> requiredProjectNames = new Parameters(dp).keySet();
236 List<DependencyContributor> dcs = getPlugins(DependencyContributor.class);
237 for (DependencyContributor dc : dcs)
238 dc.addDependencies(this, requiredProjectNames);
239
240 for (String p : requiredProjectNames) {
241 Project required = getWorkspace().getProject(p);
242 if (required == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000243 msgs.MissingDependson_(p);
Stuart McCullochbb014372012-06-07 21:57:32 +0000244 else {
245 dependencies.add(required);
246 }
247
248 }
249
250 // We have two paths that consists of repo files, projects,
251 // or some other stuff. The doPath routine adds them to the
252 // path and extracts the projects so we can build them
253 // before.
254
255 doPath(buildpath, dependencies, parseBuildpath(), bootclasspath);
256 doPath(testpath, dependencies, parseTestpath(), bootclasspath);
257 if (!delayRunDependencies) {
258 doPath(runpath, dependencies, parseRunpath(), null);
259 doPath(runbundles, dependencies, parseRunbundles(), null);
260 }
261
262 // We now know all dependent projects. But we also depend
263 // on whatever those projects depend on. This creates an
264 // ordered list without any duplicates. This of course
265 // assumes
266 // that there is no circularity. However, this is checked
267 // by the inPrepare flag, will throw an exception if we
268 // are circular.
269
270 Set<Project> done = new HashSet<Project>();
271 done.add(this);
272 allsourcepath.addAll(sourcepath);
273
274 for (Project project : dependencies)
275 project.traverse(dependson, done);
276
277 for (Project project : dependson) {
278 allsourcepath.addAll(project.getSourcePath());
279 }
280 if (isOk())
281 preparedPaths = true;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000282 }
283 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000284 inPrepare = false;
285 }
286 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000287 }
288 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000289 trail.remove(this);
290 }
291 }
292
Stuart McCullochffa8aaf2012-06-17 20:38:35 +0000293 /**
294 * @return
295 */
296 private File getOutput0() {
297 return getFile(getProperty("bin", "bin")).getAbsoluteFile();
298 }
299
300 /**
301 *
302 */
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000303 private File getTarget0() throws IOException {
Stuart McCullochffa8aaf2012-06-17 20:38:35 +0000304 File target = getFile(getProperty("target", "generated"));
305 if (!target.exists()) {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000306 if (!target.mkdirs()) {
307 throw new IOException("Could not create directory " + target);
308 }
Stuart McCullochffa8aaf2012-06-17 20:38:35 +0000309 getWorkspace().changedFile(target);
310 }
311 return target;
312 }
313
Stuart McCullochbb014372012-06-07 21:57:32 +0000314 public File getSrc() {
315 return new File(getBase(), getProperty("src", "src"));
316 }
317
318 private void traverse(Collection<Project> dependencies, Set<Project> visited) throws Exception {
319 if (visited.contains(this))
320 return;
321
322 visited.add(this);
323
324 for (Project project : getDependson())
325 project.traverse(dependencies, visited);
326
327 dependencies.add(this);
328 }
329
330 /**
331 * Iterate over the entries and place the projects on the projects list and
332 * all the files of the entries on the resultpath.
333 *
334 * @param resultpath
335 * The list that gets all the files
336 * @param projects
337 * The list that gets any projects that are entries
338 * @param entries
339 * The input list of classpath entries
340 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000341 private void doPath(Collection<Container> resultpath, Collection<Project> projects, Collection<Container> entries,
342 Collection<Container> bootclasspath) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000343 for (Container cpe : entries) {
344 if (cpe.getError() != null)
345 error(cpe.getError());
346 else {
347 if (cpe.getType() == Container.TYPE.PROJECT) {
348 projects.add(cpe.getProject());
349 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000350 if (bootclasspath != null
351 && (cpe.getBundleSymbolicName().startsWith("ee.") || cpe.getAttributes().containsKey("boot")))
Stuart McCullochbb014372012-06-07 21:57:32 +0000352 bootclasspath.add(cpe);
353 else
354 resultpath.add(cpe);
355 }
356 }
357 }
358
359 /**
360 * Parse the list of bundles that are a prerequisite to this project.
Stuart McCullochbb014372012-06-07 21:57:32 +0000361 * Bundles are listed in repo specific names. So we just let our repo
362 * plugins iterate over the list of bundles and we get the highest version
363 * from them.
364 *
365 * @return
366 */
367
368 private List<Container> parseBuildpath() throws Exception {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000369 List<Container> bundles = getBundles(Strategy.LOWEST, getProperty(Constants.BUILDPATH), Constants.BUILDPATH);
370 appendPackages(Strategy.LOWEST, getProperty(Constants.BUILDPACKAGES), bundles, ResolverMode.build);
Stuart McCullochbb014372012-06-07 21:57:32 +0000371 return bundles;
372 }
373
374 private List<Container> parseRunpath() throws Exception {
375 return getBundles(Strategy.HIGHEST, getProperty(Constants.RUNPATH), Constants.RUNPATH);
376 }
377
378 private List<Container> parseRunbundles() throws Exception {
379 return getBundles(Strategy.HIGHEST, getProperty(Constants.RUNBUNDLES), Constants.RUNBUNDLES);
380 }
381
382 private List<Container> parseTestpath() throws Exception {
383 return getBundles(Strategy.HIGHEST, getProperty(Constants.TESTPATH), Constants.TESTPATH);
384 }
385
386 /**
387 * Analyze the header and return a list of files that should be on the
388 * build, test or some other path. The list is assumed to be a list of bsns
389 * with a version specification. The special case of version=project
390 * indicates there is a project in the same workspace. The path to the
391 * output directory is calculated. The default directory ${bin} can be
392 * overridden with the output attribute.
393 *
394 * @param strategy
395 * STRATEGY_LOWEST or STRATEGY_HIGHEST
396 * @param spec
397 * The header
398 * @return
399 */
400
Stuart McCulloch2286f232012-06-15 13:27:53 +0000401 public List<Container> getBundles(Strategy strategyx, String spec, String source) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000402 List<Container> result = new ArrayList<Container>();
403 Parameters bundles = new Parameters(spec);
404
405 try {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000406 for (Iterator<Entry<String,Attrs>> i = bundles.entrySet().iterator(); i.hasNext();) {
407 Entry<String,Attrs> entry = i.next();
408 String bsn = removeDuplicateMarker(entry.getKey());
409 Map<String,String> attrs = entry.getValue();
Stuart McCullochbb014372012-06-07 21:57:32 +0000410
411 Container found = null;
412
413 String versionRange = attrs.get("version");
414
415 if (versionRange != null) {
416 if (versionRange.equals("latest") || versionRange.equals("snapshot")) {
417 found = getBundle(bsn, versionRange, strategyx, attrs);
418 }
419 }
420 if (found == null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000421 if (versionRange != null && (versionRange.equals("project") || versionRange.equals("latest"))) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000422 Project project = getWorkspace().getProject(bsn);
423 if (project != null && project.exists()) {
424 File f = project.getOutput();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000425 found = new Container(project, bsn, versionRange, Container.TYPE.PROJECT, f, null, attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +0000426 } else {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000427 msgs.NoSuchProject(bsn, spec);
Stuart McCullochbb014372012-06-07 21:57:32 +0000428 continue;
429 }
430 } else if (versionRange != null && versionRange.equals("file")) {
431 File f = getFile(bsn);
432 String error = null;
433 if (!f.exists())
434 error = "File does not exist: " + f.getAbsolutePath();
435 if (f.getName().endsWith(".lib")) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000436 found = new Container(this, bsn, "file", Container.TYPE.LIBRARY, f, error, attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +0000437 } else {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000438 found = new Container(this, bsn, "file", Container.TYPE.EXTERNAL, f, error, attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +0000439 }
440 } else {
441 found = getBundle(bsn, versionRange, strategyx, attrs);
442 }
443 }
444
445 if (found != null) {
446 List<Container> libs = found.getMembers();
447 for (Container cc : libs) {
448 if (result.contains(cc))
Stuart McCulloch2286f232012-06-15 13:27:53 +0000449 warning("Multiple bundles with the same final URL: %s, dropped duplicate", cc);
450 else
451 result.add(cc);
Stuart McCullochbb014372012-06-07 21:57:32 +0000452 }
453 } else {
454 // Oops, not a bundle in sight :-(
Stuart McCulloch2286f232012-06-15 13:27:53 +0000455 Container x = new Container(this, bsn, versionRange, Container.TYPE.ERROR, null, bsn + ";version="
456 + versionRange + " not found", attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +0000457 result.add(x);
458 warning("Can not find URL for bsn " + bsn);
459 }
460 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000461 }
462 catch (CircularDependencyException e) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000463 String message = e.getMessage();
464 if (source != null)
465 message = String.format("%s (from property: %s)", message, source);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000466 msgs.CircularDependencyContext_Message_(getName(), message);
467 }
468 catch (Exception e) {
469 msgs.Unexpected_Error_(spec, e);
Stuart McCullochbb014372012-06-07 21:57:32 +0000470 }
471 return result;
472 }
473
474 /**
475 * Just calls a new method with a default parm.
476 *
477 * @throws Exception
Stuart McCullochbb014372012-06-07 21:57:32 +0000478 */
479 Collection<Container> getBundles(Strategy strategy, String spec) throws Exception {
480 return getBundles(strategy, spec, null);
481 }
482
483 /**
484 * Calculates the containers required to fulfil the {@code -buildpackages}
485 * instruction, and appends them to the existing list of containers.
486 *
487 * @param strategyx
488 * The package-version disambiguation strategy.
489 * @param spec
490 * The value of the @{code -buildpackages} instruction.
491 * @throws Exception
492 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000493 public void appendPackages(Strategy strategyx, String spec, List<Container> resolvedBundles, ResolverMode mode)
494 throws Exception {
495 Map<File,Container> pkgResolvedBundles = new HashMap<File,Container>();
Stuart McCullochbb014372012-06-07 21:57:32 +0000496
Stuart McCulloch2286f232012-06-15 13:27:53 +0000497 List<Entry<String,Attrs>> queue = new LinkedList<Map.Entry<String,Attrs>>();
Stuart McCullochbb014372012-06-07 21:57:32 +0000498 queue.addAll(new Parameters(spec).entrySet());
499
500 while (!queue.isEmpty()) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000501 Entry<String,Attrs> entry = queue.remove(0);
Stuart McCullochbb014372012-06-07 21:57:32 +0000502
503 String pkgName = entry.getKey();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000504 Map<String,String> attrs = entry.getValue();
Stuart McCullochbb014372012-06-07 21:57:32 +0000505
506 Container found = null;
507
508 String versionRange = attrs.get(Constants.VERSION_ATTRIBUTE);
509 if ("latest".equals(versionRange) || "snapshot".equals(versionRange))
510 found = getPackage(pkgName, versionRange, strategyx, attrs, mode);
511
512 if (found == null)
513 found = getPackage(pkgName, versionRange, strategyx, attrs, mode);
514
515 if (found != null) {
516 if (resolvedBundles.contains(found)) {
517 // Don't add his bundle because it was already included
518 // using -buildpath
519 } else {
520 List<Container> libs = found.getMembers();
521 for (Container cc : libs) {
522 Container existing = pkgResolvedBundles.get(cc.file);
523 if (existing != null)
524 addToPackageList(existing, attrs.get("packages"));
525 else {
526 addToPackageList(cc, attrs.get("packages"));
527 pkgResolvedBundles.put(cc.file, cc);
528 }
529
530 String importUses = cc.getAttributes().get("import-uses");
531 if (importUses != null)
532 queue.addAll(0, new Parameters(importUses).entrySet());
533 }
534 }
535 } else {
536 // Unable to resolve
Stuart McCulloch2286f232012-06-15 13:27:53 +0000537 Container x = new Container(this, "X", versionRange, Container.TYPE.ERROR, null, "package " + pkgName
538 + ";version=" + versionRange + " not found", attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +0000539 resolvedBundles.add(x);
540 warning("Can not find URL for package " + pkgName);
541 }
542 }
543
544 for (Container container : pkgResolvedBundles.values()) {
545 resolvedBundles.add(container);
546 }
547 }
548
549 static void mergeNames(String names, Set<String> set) {
550 StringTokenizer tokenizer = new StringTokenizer(names, ",");
551 while (tokenizer.hasMoreTokens())
552 set.add(tokenizer.nextToken().trim());
553 }
554
555 static String flatten(Set<String> names) {
556 StringBuilder builder = new StringBuilder();
557 boolean first = true;
558 for (String name : names) {
559 if (!first)
560 builder.append(',');
561 builder.append(name);
562 first = false;
563 }
564 return builder.toString();
565 }
566
567 static void addToPackageList(Container container, String newPackageNames) {
568 Set<String> merged = new HashSet<String>();
569
570 String packageListStr = container.attributes.get("packages");
571 if (packageListStr != null)
572 mergeNames(packageListStr, merged);
573 if (newPackageNames != null)
574 mergeNames(newPackageNames, merged);
575
576 container.putAttribute("packages", flatten(merged));
577 }
578
579 /**
580 * Find a container to fulfil a package requirement
581 *
582 * @param packageName
583 * The package required
584 * @param range
585 * The package version range required
586 * @param strategyx
587 * The package-version disambiguation strategy
588 * @param attrs
589 * Other attributes specified by the search.
590 * @return
591 * @throws Exception
592 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000593 public Container getPackage(String packageName, String range, Strategy strategyx, Map<String,String> attrs,
594 ResolverMode mode) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000595 if ("snapshot".equals(range))
596 return new Container(this, "", range, Container.TYPE.ERROR, null,
597 "snapshot not supported for package lookups", null);
598
599 if (attrs == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000600 attrs = new HashMap<String,String>(2);
Stuart McCullochbb014372012-06-07 21:57:32 +0000601 attrs.put("package", packageName);
602 attrs.put("mode", mode.name());
603
604 Strategy useStrategy = findStrategy(attrs, strategyx, range);
605
606 List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
607 for (RepositoryPlugin plugin : plugins) {
608 try {
609 File result = plugin.get(null, range, useStrategy, attrs);
610 if (result != null) {
611 if (result.getName().endsWith("lib"))
Stuart McCulloch2286f232012-06-15 13:27:53 +0000612 return new Container(this, result.getName(), range, Container.TYPE.LIBRARY, result, null, attrs);
613 return new Container(this, result.getName(), range, Container.TYPE.REPO, result, null, attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +0000614 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000615 }
616 catch (Exception e) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000617 // Ignore... lots of repos will fail here
618 }
619 }
620
Stuart McCulloch2286f232012-06-15 13:27:53 +0000621 return new Container(this, "X", range, Container.TYPE.ERROR, null, "package " + packageName + ";version="
622 + range + " Not found in " + plugins, null);
Stuart McCullochbb014372012-06-07 21:57:32 +0000623 }
624
Stuart McCulloch2286f232012-06-15 13:27:53 +0000625 private Strategy findStrategy(Map<String,String> attrs, Strategy defaultStrategy, String versionRange) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000626 Strategy useStrategy = defaultStrategy;
627 String overrideStrategy = attrs.get("strategy");
628 if (overrideStrategy != null) {
629 if ("highest".equalsIgnoreCase(overrideStrategy))
630 useStrategy = Strategy.HIGHEST;
631 else if ("lowest".equalsIgnoreCase(overrideStrategy))
632 useStrategy = Strategy.LOWEST;
633 else if ("exact".equalsIgnoreCase(overrideStrategy))
634 useStrategy = Strategy.EXACT;
635 }
636 if ("latest".equals(versionRange))
637 useStrategy = Strategy.HIGHEST;
638 return useStrategy;
639 }
640
641 /**
642 * The user selected pom in a path. This will place the pom as well as its
643 * dependencies on the list
644 *
645 * @param strategyx
646 * the strategy to use.
647 * @param result
648 * The list of result containers
649 * @param attrs
650 * The attributes
651 * @throws Exception
652 * anything goes wrong
653 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000654 public void doMavenPom(Strategy strategyx, List<Container> result, String action) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000655 File pomFile = getFile("pom.xml");
656 if (!pomFile.isFile())
Stuart McCulloch2286f232012-06-15 13:27:53 +0000657 msgs.MissingPom();
Stuart McCullochbb014372012-06-07 21:57:32 +0000658 else {
659 ProjectPom pom = getWorkspace().getMaven().createProjectModel(pomFile);
660 if (action == null)
661 action = "compile";
662 Pom.Scope act = Pom.Scope.valueOf(action);
663 Set<Pom> dependencies = pom.getDependencies(act);
664 for (Pom sub : dependencies) {
665 File artifact = sub.getArtifact();
666 Container container = new Container(artifact);
667 result.add(container);
668 }
669 }
670 }
671
672 public Collection<Project> getDependson() throws Exception {
673 prepare();
674 return dependson;
675 }
676
677 public Collection<Container> getBuildpath() throws Exception {
678 prepare();
679 return buildpath;
680 }
681
682 public Collection<Container> getTestpath() throws Exception {
683 prepare();
684 return testpath;
685 }
686
687 /**
688 * Handle dependencies for paths that are calculated on demand.
689 *
690 * @param testpath2
691 * @param parseTestpath
692 */
693 private void justInTime(Collection<Container> path, List<Container> entries) {
694 if (delayRunDependencies && path.isEmpty())
695 doPath(path, dependson, entries, null);
696 }
697
698 public Collection<Container> getRunpath() throws Exception {
699 prepare();
700 justInTime(runpath, parseRunpath());
701 return runpath;
702 }
703
704 public Collection<Container> getRunbundles() throws Exception {
705 prepare();
706 justInTime(runbundles, parseRunbundles());
707 return runbundles;
708 }
709
710 public File getRunStorage() throws Exception {
711 prepare();
712 return runstorage;
713 }
714
715 public boolean getRunBuilds() {
716 boolean result;
717 String runBuildsStr = getProperty(Constants.RUNBUILDS);
718 if (runBuildsStr == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000719 result = !getPropertiesFile().getName().toLowerCase().endsWith(Constants.DEFAULT_BNDRUN_EXTENSION);
Stuart McCullochbb014372012-06-07 21:57:32 +0000720 else
721 result = Boolean.parseBoolean(runBuildsStr);
722 return result;
723 }
724
725 public Collection<File> getSourcePath() throws Exception {
726 prepare();
727 return sourcepath;
728 }
729
730 public Collection<File> getAllsourcepath() throws Exception {
731 prepare();
732 return allsourcepath;
733 }
734
735 public Collection<Container> getBootclasspath() throws Exception {
736 prepare();
737 return bootclasspath;
738 }
739
740 public File getOutput() throws Exception {
741 prepare();
742 return output;
743 }
744
745 private void doEclipseClasspath() throws Exception {
746 EclipseClasspath eclipse = new EclipseClasspath(this, getWorkspace().getBase(), getBase());
747 eclipse.setRecurse(false);
748
749 // We get the file directories but in this case we need
750 // to tell ant that the project names
751 for (File dependent : eclipse.getDependents()) {
752 Project required = workspace.getProject(dependent.getName());
753 dependson.add(required);
754 }
755 for (File f : eclipse.getClasspath()) {
756 buildpath.add(new Container(f));
757 }
758 for (File f : eclipse.getBootclasspath()) {
759 bootclasspath.add(new Container(f));
760 }
761 sourcepath.addAll(eclipse.getSourcepath());
762 allsourcepath.addAll(eclipse.getAllSources());
763 output = eclipse.getOutput();
764 }
765
766 public String _p_dependson(String args[]) throws Exception {
767 return list(args, toFiles(getDependson()));
768 }
769
Stuart McCulloch2286f232012-06-15 13:27:53 +0000770 private Collection< ? > toFiles(Collection<Project> projects) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000771 List<File> files = new ArrayList<File>();
772 for (Project p : projects) {
773 files.add(p.getBase());
774 }
775 return files;
776 }
777
778 public String _p_buildpath(String args[]) throws Exception {
779 return list(args, getBuildpath());
780 }
781
782 public String _p_testpath(String args[]) throws Exception {
783 return list(args, getRunpath());
784 }
785
786 public String _p_sourcepath(String args[]) throws Exception {
787 return list(args, getSourcePath());
788 }
789
790 public String _p_allsourcepath(String args[]) throws Exception {
791 return list(args, getAllsourcepath());
792 }
793
794 public String _p_bootclasspath(String args[]) throws Exception {
795 return list(args, getBootclasspath());
796 }
797
798 public String _p_output(String args[]) throws Exception {
799 if (args.length != 1)
800 throw new IllegalArgumentException("${output} should not have arguments");
801 return getOutput().getAbsolutePath();
802 }
803
Stuart McCulloch2286f232012-06-15 13:27:53 +0000804 private String list(String[] args, Collection< ? > list) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000805 if (args.length > 3)
806 throw new IllegalArgumentException("${" + args[0]
Stuart McCulloch2286f232012-06-15 13:27:53 +0000807 + "[;<separator>]} can only take a separator as argument, has " + Arrays.toString(args));
Stuart McCullochbb014372012-06-07 21:57:32 +0000808
809 String separator = ",";
810
811 if (args.length == 2) {
812 separator = args[1];
813 }
814
815 return join(list, separator);
816 }
817
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000818 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +0000819 protected Object[] getMacroDomains() {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000820 return new Object[] {
821 workspace
822 };
Stuart McCullochbb014372012-06-07 21:57:32 +0000823 }
824
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000825 public File release(String jarName, InputStream jarStream) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000826 String name = getProperty(Constants.RELEASEREPO);
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000827 return release(name, jarName, jarStream);
Stuart McCullochbb014372012-06-07 21:57:32 +0000828 }
829
830 /**
831 * Release
832 *
833 * @param name
834 * The repository name
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000835 * @param jarName
836 * @param jarStream
Stuart McCullochbb014372012-06-07 21:57:32 +0000837 * @return
838 * @throws Exception
839 */
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000840 public File release(String name, String jarName, InputStream jarStream) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000841 trace("release %s", name);
842 List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
843 RepositoryPlugin rp = null;
844 for (RepositoryPlugin plugin : plugins) {
845 if (!plugin.canWrite()) {
846 continue;
847 }
848 if (name == null) {
849 rp = plugin;
850 break;
851 } else if (name.equals(plugin.getName())) {
852 rp = plugin;
853 break;
854 }
855 }
856
857 if (rp != null) {
858 try {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000859 PutResult r = rp.put(jarStream, new RepositoryPlugin.PutOptions());
860 trace("Released %s to %s in repository %s", jarName, r.artifact, rp);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000861 }
862 catch (Exception e) {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000863 msgs.Release_Into_Exception_(jarName, rp, e);
Stuart McCullochbb014372012-06-07 21:57:32 +0000864 }
865 } else if (name == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000866 msgs.NoNameForReleaseRepository();
Stuart McCullochbb014372012-06-07 21:57:32 +0000867 else
Stuart McCulloch2286f232012-06-15 13:27:53 +0000868 msgs.ReleaseRepository_NotFoundIn_(name, plugins);
Stuart McCullochbb014372012-06-07 21:57:32 +0000869
870 return null;
871
872 }
873
874 public void release(boolean test) throws Exception {
875 String name = getProperty(Constants.RELEASEREPO);
876 release(name, test);
877 }
878
879 /**
880 * Release
881 *
882 * @param name
883 * The respository name
884 * @param test
885 * Run testcases
886 * @throws Exception
887 */
888 public void release(String name, boolean test) throws Exception {
889 trace("release");
890 File[] jars = build(test);
891 // If build fails jars will be null
892 if (jars == null) {
893 trace("no jars being build");
894 return;
895 }
896 trace("build ", Arrays.toString(jars));
897 for (File jar : jars) {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000898 release(name, jar.getName(), new BufferedInputStream(new FileInputStream(jar)));
Stuart McCullochbb014372012-06-07 21:57:32 +0000899 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000900 }
901
902 /**
903 * Get a bundle from one of the plugin repositories. If an exact version is
904 * required we just return the first repository found (in declaration order
905 * in the build.bnd file).
906 *
907 * @param bsn
908 * The bundle symbolic name
909 * @param range
910 * The version range
911 * @param lowest
912 * set to LOWEST or HIGHEST
913 * @return the file object that points to the bundle or null if not found
914 * @throws Exception
915 * when something goes wrong
916 */
917
Stuart McCulloch2286f232012-06-15 13:27:53 +0000918 public Container getBundle(String bsn, String range, Strategy strategy, Map<String,String> attrs) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000919
920 if (range == null)
921 range = "0";
922
923 if ("snapshot".equals(range)) {
924 return getBundleFromProject(bsn, attrs);
925 }
926
927 Strategy useStrategy = strategy;
928
929 if ("latest".equals(range)) {
930 Container c = getBundleFromProject(bsn, attrs);
931 if (c != null)
932 return c;
933
934 useStrategy = Strategy.HIGHEST;
935 }
936
937 useStrategy = overrideStrategy(attrs, useStrategy);
938
939 List<RepositoryPlugin> plugins = workspace.getRepositories();
940
941 if (useStrategy == Strategy.EXACT) {
942
943 // For an exact range we just iterate over the repos
944 // and return the first we find.
945
946 for (RepositoryPlugin plugin : plugins) {
947 File result = plugin.get(bsn, range, Strategy.EXACT, attrs);
948 if (result != null)
949 return toContainer(bsn, range, attrs, result);
950 }
951 } else {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000952 VersionRange versionRange = "latest".equals(range) ? new VersionRange("0") : new VersionRange(range);
Stuart McCullochbb014372012-06-07 21:57:32 +0000953
954 // We have a range search. Gather all the versions in all the repos
955 // and make a decision on that choice. If the same version is found
956 // in
957 // multiple repos we take the first
958
Stuart McCulloch2286f232012-06-15 13:27:53 +0000959 SortedMap<Version,RepositoryPlugin> versions = new TreeMap<Version,RepositoryPlugin>();
Stuart McCullochbb014372012-06-07 21:57:32 +0000960 for (RepositoryPlugin plugin : plugins) {
961 try {
962 List<Version> vs = plugin.versions(bsn);
963 if (vs != null) {
964 for (Version v : vs) {
965 if (!versions.containsKey(v) && versionRange.includes(v))
966 versions.put(v, plugin);
967 }
968 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000969 }
970 catch (UnsupportedOperationException ose) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000971 // We have a plugin that cannot list versions, try
972 // if it has this specific version
973 // The main reaosn for this code was the Maven Remote
974 // Repository
975 // To query, we must have a real version
976 if (!versions.isEmpty() && Verifier.isVersion(range)) {
977 File file = plugin.get(bsn, range, useStrategy, attrs);
978 // and the entry must exist
979 // if it does, return this as a result
980 if (file != null)
981 return toContainer(bsn, range, attrs, file);
982 }
983 }
984 }
985
986 // Verify if we found any, if so, we use the strategy to pick
987 // the first or last
988
989 if (!versions.isEmpty()) {
990 Version provider = null;
991
992 switch (useStrategy) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000993 case HIGHEST :
994 provider = versions.lastKey();
995 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000996
Stuart McCulloch2286f232012-06-15 13:27:53 +0000997 case LOWEST :
998 provider = versions.firstKey();
999 break;
Stuart McCulloch151384c2012-06-18 11:15:15 +00001000 case EXACT :
1001 // TODO need to handle exact better
1002 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001003 }
1004 if (provider != null) {
1005 RepositoryPlugin repo = versions.get(provider);
1006 String version = provider.toString();
1007 File result = repo.get(bsn, version, Strategy.EXACT, attrs);
1008 if (result != null)
1009 return toContainer(bsn, version, attrs, result);
1010 } else
Stuart McCulloch2286f232012-06-15 13:27:53 +00001011 msgs.FoundVersions_ForStrategy_ButNoProvider(versions, useStrategy);
Stuart McCullochbb014372012-06-07 21:57:32 +00001012 }
1013 }
1014
1015 //
1016 // If we get this far we ran into an error somewhere
1017
Stuart McCulloch2286f232012-06-15 13:27:53 +00001018 return new Container(this, bsn, range, Container.TYPE.ERROR, null, bsn + ";version=" + range + " Not found in "
1019 + plugins, null);
Stuart McCullochbb014372012-06-07 21:57:32 +00001020
1021 }
1022
1023 /**
1024 * @param attrs
1025 * @param useStrategy
1026 * @return
1027 */
Stuart McCulloch2286f232012-06-15 13:27:53 +00001028 protected Strategy overrideStrategy(Map<String,String> attrs, Strategy useStrategy) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001029 if (attrs != null) {
1030 String overrideStrategy = attrs.get("strategy");
1031
1032 if (overrideStrategy != null) {
1033 if ("highest".equalsIgnoreCase(overrideStrategy))
1034 useStrategy = Strategy.HIGHEST;
1035 else if ("lowest".equalsIgnoreCase(overrideStrategy))
1036 useStrategy = Strategy.LOWEST;
1037 else if ("exact".equalsIgnoreCase(overrideStrategy))
1038 useStrategy = Strategy.EXACT;
1039 }
1040 }
1041 return useStrategy;
1042 }
1043
1044 /**
1045 * @param bsn
1046 * @param range
1047 * @param attrs
1048 * @param result
1049 * @return
1050 */
Stuart McCulloch2286f232012-06-15 13:27:53 +00001051 protected Container toContainer(String bsn, String range, Map<String,String> attrs, File result) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001052 File f = result;
1053 if (f == null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001054 msgs.ConfusedNoContainerFile();
Stuart McCullochbb014372012-06-07 21:57:32 +00001055 f = new File("was null");
1056 }
1057 if (f.getName().endsWith("lib"))
1058 return new Container(this, bsn, range, Container.TYPE.LIBRARY, f, null, attrs);
Stuart McCullochd4826102012-06-26 16:34:24 +00001059 return new Container(this, bsn, range, Container.TYPE.REPO, f, null, attrs);
Stuart McCullochbb014372012-06-07 21:57:32 +00001060 }
1061
1062 /**
1063 * Look for the bundle in the workspace. The premise is that the bsn must
1064 * start with the project name.
1065 *
1066 * @param bsn
1067 * The bsn
1068 * @param attrs
1069 * Any attributes
1070 * @return
1071 * @throws Exception
1072 */
Stuart McCulloch2286f232012-06-15 13:27:53 +00001073 private Container getBundleFromProject(String bsn, Map<String,String> attrs) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +00001074 String pname = bsn;
1075 while (true) {
1076 Project p = getWorkspace().getProject(pname);
1077 if (p != null && p.isValid()) {
1078 Container c = p.getDeliverable(bsn, attrs);
1079 return c;
1080 }
1081
1082 int n = pname.lastIndexOf('.');
1083 if (n <= 0)
1084 return null;
1085 pname = pname.substring(0, n);
1086 }
1087 }
1088
1089 /**
1090 * Deploy the file (which must be a bundle) into the repository.
1091 *
1092 * @param name
1093 * The repository name
1094 * @param file
1095 * bundle
1096 */
1097 public void deploy(String name, File file) throws Exception {
1098 List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
1099
1100 RepositoryPlugin rp = null;
1101 for (RepositoryPlugin plugin : plugins) {
1102 if (!plugin.canWrite()) {
1103 continue;
1104 }
1105 if (name == null) {
1106 rp = plugin;
1107 break;
1108 } else if (name.equals(plugin.getName())) {
1109 rp = plugin;
1110 break;
1111 }
1112 }
1113
1114 if (rp != null) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001115 try {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001116 rp.put(new BufferedInputStream(new FileInputStream(file)), new RepositoryPlugin.PutOptions());
Stuart McCullochbb014372012-06-07 21:57:32 +00001117 return;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001118 }
1119 catch (Exception e) {
1120 msgs.DeployingFile_On_Exception_(file, rp.getName(), e);
1121 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001122 return;
1123 }
1124 trace("No repo found " + file);
1125 throw new IllegalArgumentException("No repository found for " + file);
1126 }
1127
1128 /**
1129 * Deploy the file (which must be a bundle) into the repository.
1130 *
1131 * @param file
1132 * bundle
1133 */
1134 public void deploy(File file) throws Exception {
1135 String name = getProperty(Constants.DEPLOYREPO);
1136 deploy(name, file);
1137 }
1138
1139 /**
1140 * Deploy the current project to a repository
1141 *
1142 * @throws Exception
1143 */
1144 public void deploy() throws Exception {
1145 Parameters deploy = new Parameters(getProperty(DEPLOY));
1146 if (deploy.isEmpty()) {
1147 warning("Deploying but %s is not set to any repo", DEPLOY);
1148 return;
1149 }
1150 File[] outputs = getBuildFiles();
1151 for (File output : outputs) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001152 for (Deploy d : getPlugins(Deploy.class)) {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001153 trace("Deploying %s to: %s", output.getName(), d);
Stuart McCullochbb014372012-06-07 21:57:32 +00001154 try {
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001155 if (d.deploy(this, output.getName(), new BufferedInputStream(new FileInputStream(output))))
Stuart McCullochbb014372012-06-07 21:57:32 +00001156 trace("deployed %s successfully to %s", output, d);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001157 }
1158 catch (Exception e) {
1159 msgs.Deploying(e);
Stuart McCullochbb014372012-06-07 21:57:32 +00001160 }
1161 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001162 }
1163 }
1164
1165 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +00001166 * Macro access to the repository ${repo;<bsn>[;<version>[;<low|high>]]}
Stuart McCullochbb014372012-06-07 21:57:32 +00001167 */
1168
Stuart McCulloch2286f232012-06-15 13:27:53 +00001169 static String _repoHelp = "${repo ';'<bsn> [ ; <version> [; ('HIGHEST'|'LOWEST')]}";
1170
Stuart McCullochbb014372012-06-07 21:57:32 +00001171 public String _repo(String args[]) throws Exception {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001172 if (args.length < 2) {
1173 msgs.RepoTooFewArguments(_repoHelp, args);
1174 return null;
1175 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001176
1177 String bsns = args[1];
1178 String version = null;
1179 Strategy strategy = Strategy.HIGHEST;
1180
1181 if (args.length > 2) {
1182 version = args[2];
1183 if (args.length == 4) {
1184 if (args[3].equalsIgnoreCase("HIGHEST"))
1185 strategy = Strategy.HIGHEST;
1186 else if (args[3].equalsIgnoreCase("LOWEST"))
1187 strategy = Strategy.LOWEST;
1188 else if (args[3].equalsIgnoreCase("EXACT"))
1189 strategy = Strategy.EXACT;
1190 else
Stuart McCulloch2286f232012-06-15 13:27:53 +00001191 msgs.InvalidStrategy(_repoHelp, args);
Stuart McCullochbb014372012-06-07 21:57:32 +00001192 }
1193 }
1194
1195 Collection<String> parts = split(bsns);
1196 List<String> paths = new ArrayList<String>();
1197
1198 for (String bsn : parts) {
1199 Container container = getBundle(bsn, version, strategy, null);
1200 add(paths, container);
1201 }
1202 return join(paths);
1203 }
1204
1205 private void add(List<String> paths, Container container) throws Exception {
1206 if (container.getType() == Container.TYPE.LIBRARY) {
1207 List<Container> members = container.getMembers();
1208 for (Container sub : members) {
1209 add(paths, sub);
1210 }
1211 } else {
1212 if (container.getError() == null)
1213 paths.add(container.getFile().getAbsolutePath());
1214 else {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001215 paths.add("<<${repo} = " + container.getBundleSymbolicName() + "-" + container.getVersion() + " : "
1216 + container.getError() + ">>");
Stuart McCullochbb014372012-06-07 21:57:32 +00001217
1218 if (isPedantic()) {
1219 warning("Could not expand repo path request: %s ", container);
1220 }
1221 }
1222
1223 }
1224 }
1225
1226 public File getTarget() throws Exception {
1227 prepare();
1228 return target;
1229 }
1230
1231 /**
1232 * This is the external method that will pre-build any dependencies if it is
1233 * out of date.
1234 *
1235 * @param underTest
1236 * @return
1237 * @throws Exception
1238 */
1239 public File[] build(boolean underTest) throws Exception {
1240 if (isNoBundles())
1241 return null;
1242
1243 if (getProperty("-nope") != null) {
1244 warning("Please replace -nope with %s", NOBUNDLES);
1245 return null;
1246 }
1247
1248 if (isStale()) {
Stuart McCullochd4826102012-06-26 16:34:24 +00001249 trace("building " + this);
Stuart McCullochbb014372012-06-07 21:57:32 +00001250 files = buildLocal(underTest);
1251 }
1252
1253 return files;
1254 }
1255
1256 /**
1257 * Return the files
1258 */
1259
1260 public File[] getFiles() {
1261 return files;
1262 }
1263
1264 /**
1265 * Check if this project needs building. This is defined as:
Stuart McCullochbb014372012-06-07 21:57:32 +00001266 */
1267 public boolean isStale() throws Exception {
Stuart McCulloch151384c2012-06-18 11:15:15 +00001268 if ( workspace.isOffline()) {
1269 trace("working %s offline, so always stale", this);
1270 return true;
1271 }
1272
Stuart McCulloch2286f232012-06-15 13:27:53 +00001273 Set<Project> visited = new HashSet<Project>();
1274 return isStale(visited);
1275 }
1276
1277 boolean isStale(Set<Project> visited) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +00001278 // When we do not generate anything ...
1279 if (isNoBundles())
1280 return false;
1281
Stuart McCulloch2286f232012-06-15 13:27:53 +00001282 if (visited.contains(this)) {
1283 msgs.CircularDependencyContext_Message_(this.getName(), visited.toString());
1284 return false;
1285 }
1286
1287 visited.add(this);
1288
Stuart McCullochbb014372012-06-07 21:57:32 +00001289 long buildTime = 0;
1290
1291 files = getBuildFiles(false);
1292 if (files == null)
1293 return true;
1294
1295 for (File f : files) {
1296 if (f.lastModified() < lastModified())
1297 return true;
1298
1299 if (buildTime < f.lastModified())
1300 buildTime = f.lastModified();
1301 }
1302
1303 for (Project dependency : getDependson()) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001304 if (dependency == this)
1305 continue;
1306
Stuart McCullochbb014372012-06-07 21:57:32 +00001307 if (dependency.isStale())
1308 return true;
1309
1310 if (dependency.isNoBundles())
1311 continue;
1312
1313 File[] deps = dependency.getBuildFiles();
1314 for (File f : deps) {
1315 if (f.lastModified() >= buildTime)
1316 return true;
1317 }
1318 }
Stuart McCulloch151384c2012-06-18 11:15:15 +00001319
Stuart McCullochbb014372012-06-07 21:57:32 +00001320 return false;
1321 }
1322
1323 /**
1324 * This method must only be called when it is sure that the project has been
Stuart McCulloch2286f232012-06-15 13:27:53 +00001325 * build before in the same session. It is a bit yucky, but ant creates
1326 * different class spaces which makes it hard to detect we already build it.
Stuart McCullochbb014372012-06-07 21:57:32 +00001327 * This method remembers the files in the appropriate instance vars.
1328 *
1329 * @return
1330 */
1331
1332 public File[] getBuildFiles() throws Exception {
1333 return getBuildFiles(true);
1334 }
1335
1336 public File[] getBuildFiles(boolean buildIfAbsent) throws Exception {
1337 if (files != null)
1338 return files;
1339
1340 File f = new File(getTarget(), BUILDFILES);
1341 if (f.isFile()) {
1342 BufferedReader rdr = IO.reader(f);
1343 try {
1344 List<File> files = newList();
1345 for (String s = rdr.readLine(); s != null; s = rdr.readLine()) {
1346 s = s.trim();
1347 File ff = new File(s);
1348 if (!ff.isFile()) {
1349 // Originally we warned the user
1350 // but lets just rebuild. That way
Stuart McCulloch2286f232012-06-15 13:27:53 +00001351 // the error is not noticed but
Stuart McCullochbb014372012-06-07 21:57:32 +00001352 // it seems better to correct,
1353 // See #154
1354 rdr.close();
1355 f.delete();
1356 break;
Stuart McCullochd4826102012-06-26 16:34:24 +00001357 }
1358 files.add(ff);
Stuart McCullochbb014372012-06-07 21:57:32 +00001359 }
1360 return this.files = files.toArray(new File[files.size()]);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001361 }
1362 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +00001363 rdr.close();
1364 }
1365 }
1366 if (buildIfAbsent)
1367 return files = buildLocal(false);
Stuart McCullochd4826102012-06-26 16:34:24 +00001368 return files = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001369 }
1370
1371 /**
1372 * Build without doing any dependency checking. Make sure any dependent
1373 * projects are built first.
1374 *
1375 * @param underTest
1376 * @return
1377 * @throws Exception
1378 */
1379 public File[] buildLocal(boolean underTest) throws Exception {
1380 if (isNoBundles())
1381 return null;
1382
1383 File bfs = new File(getTarget(), BUILDFILES);
1384 bfs.delete();
1385
1386 files = null;
1387 ProjectBuilder builder = getBuilder(null);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001388 try {
1389 if (underTest)
1390 builder.setProperty(Constants.UNDERTEST, "true");
1391 Jar jars[] = builder.builds();
1392 File[] files = new File[jars.length];
Stuart McCullochbb014372012-06-07 21:57:32 +00001393
Stuart McCulloch2286f232012-06-15 13:27:53 +00001394 getInfo(builder);
Stuart McCullochbb014372012-06-07 21:57:32 +00001395
Stuart McCulloch2286f232012-06-15 13:27:53 +00001396 if (isOk()) {
1397 this.files = files;
1398
1399 for (int i = 0; i < jars.length; i++) {
1400 Jar jar = jars[i];
1401 files[i] = saveBuild(jar);
Stuart McCullochbb014372012-06-07 21:57:32 +00001402 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00001403
1404 // Write out the filenames in the buildfiles file
1405 // so we can get them later evenin another process
1406 Writer fw = IO.writer(bfs);
1407 try {
1408 for (File f : files) {
1409 fw.append(f.getAbsolutePath());
1410 fw.append("\n");
1411 }
1412 }
1413 finally {
1414 fw.close();
1415 }
1416 getWorkspace().changedFile(bfs);
1417 return files;
Stuart McCullochd4826102012-06-26 16:34:24 +00001418 }
1419 return null;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001420 }
1421 finally {
1422 builder.close();
1423 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001424 }
1425
1426 /**
1427 * Answer if this project does not have any output
1428 *
1429 * @return
1430 */
1431 public boolean isNoBundles() {
1432 return getProperty(NOBUNDLES) != null;
1433 }
1434
1435 public File saveBuild(Jar jar) throws Exception {
1436 try {
1437 String bsn = jar.getName();
1438 File f = getOutputFile(bsn);
1439 String msg = "";
1440 if (!f.exists() || f.lastModified() < jar.lastModified()) {
1441 reportNewer(f.lastModified(), jar);
1442 f.delete();
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001443 File fp = f.getParentFile();
1444 if (!fp.isDirectory()) {
1445 if (!fp.exists() && !fp.mkdirs()) {
1446 throw new IOException("Could not create directory " + fp);
1447 }
1448 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001449 jar.write(f);
1450
1451 getWorkspace().changedFile(f);
1452 } else {
1453 msg = "(not modified since " + new Date(f.lastModified()) + ")";
1454 }
1455 trace(jar.getName() + " (" + f.getName() + ") " + jar.getResources().size() + " " + msg);
1456 return f;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001457 }
1458 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +00001459 jar.close();
1460 }
1461 }
1462
1463 public File getOutputFile(String bsn) throws Exception {
1464 return new File(getTarget(), bsn + ".jar");
1465 }
1466
1467 private void reportNewer(long lastModified, Jar jar) {
1468 if (isTrue(getProperty(Constants.REPORTNEWER))) {
1469 StringBuilder sb = new StringBuilder();
1470 String del = "Newer than " + new Date(lastModified);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001471 for (Map.Entry<String,Resource> entry : jar.getResources().entrySet()) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001472 if (entry.getValue().lastModified() > lastModified) {
1473 sb.append(del);
1474 del = ", \n ";
1475 sb.append(entry.getKey());
1476 }
1477 }
1478 if (sb.length() > 0)
1479 warning(sb.toString());
1480 }
1481 }
1482
1483 /**
1484 * Refresh if we are based on stale data. This also implies our workspace.
1485 */
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001486 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +00001487 public boolean refresh() {
1488 boolean changed = false;
1489 if (isCnf()) {
1490 changed = workspace.refresh();
1491 }
1492 return super.refresh() || changed;
1493 }
1494
1495 public boolean isCnf() {
1496 return getBase().getName().equals(Workspace.CNFDIR);
1497 }
1498
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001499 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +00001500 public void propertiesChanged() {
1501 super.propertiesChanged();
1502 preparedPaths = false;
1503 files = null;
1504
1505 }
1506
1507 public String getName() {
1508 return getBase().getName();
1509 }
1510
Stuart McCulloch2286f232012-06-15 13:27:53 +00001511 public Map<String,Action> getActions() {
1512 Map<String,Action> all = newMap();
1513 Map<String,Action> actions = newMap();
Stuart McCullochbb014372012-06-07 21:57:32 +00001514 fillActions(all);
1515 getWorkspace().fillActions(all);
1516
Stuart McCulloch2286f232012-06-15 13:27:53 +00001517 for (Map.Entry<String,Action> action : all.entrySet()) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001518 String key = getReplacer().process(action.getKey());
1519 if (key != null && key.trim().length() != 0)
1520 actions.put(key, action.getValue());
1521 }
1522 return actions;
1523 }
1524
Stuart McCulloch2286f232012-06-15 13:27:53 +00001525 public void fillActions(Map<String,Action> all) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001526 List<NamedAction> plugins = getPlugins(NamedAction.class);
1527 for (NamedAction a : plugins)
1528 all.put(a.getName(), a);
1529
1530 Parameters actions = new Parameters(getProperty("-actions", DEFAULT_ACTIONS));
Stuart McCulloch2286f232012-06-15 13:27:53 +00001531 for (Entry<String,Attrs> entry : actions.entrySet()) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001532 String key = Processor.removeDuplicateMarker(entry.getKey());
1533 Action action;
1534
1535 if (entry.getValue().get("script") != null) {
1536 // TODO check for the type
Stuart McCulloch2286f232012-06-15 13:27:53 +00001537 action = new ScriptAction(entry.getValue().get("type"), entry.getValue().get("script"));
Stuart McCullochbb014372012-06-07 21:57:32 +00001538 } else {
1539 action = new ReflectAction(key);
1540 }
1541 String label = entry.getValue().get("label");
1542 all.put(label.toLowerCase(), action);
1543 }
1544 }
1545
1546 public void release() throws Exception {
1547 release(false);
1548 }
1549
1550 /**
1551 * Release.
1552 *
1553 * @param name
1554 * The repository name
1555 * @throws Exception
1556 */
1557 public void release(String name) throws Exception {
1558 release(name, false);
1559 }
1560
1561 public void clean() throws Exception {
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001562 File target = getTarget0();
Stuart McCullochbb014372012-06-07 21:57:32 +00001563 if (target.isDirectory() && target.getParentFile() != null) {
1564 IO.delete(target);
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001565 if (!target.exists() && !target.mkdirs()) {
1566 throw new IOException("Could not create directory " + target);
1567 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001568 }
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001569 File output = getOutput0();
Stuart McCullochbb014372012-06-07 21:57:32 +00001570 if (getOutput().isDirectory())
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001571 IO.delete(output);
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001572 if (!output.exists() && !output.mkdirs()) {
1573 throw new IOException("Could not create directory " + output);
1574 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001575 }
1576
1577 public File[] build() throws Exception {
1578 return build(false);
1579 }
1580
1581 public void run() throws Exception {
1582 ProjectLauncher pl = getProjectLauncher();
1583 pl.setTrace(isTrace());
1584 pl.launch();
1585 }
1586
1587 public void test() throws Exception {
1588 clear();
1589 ProjectTester tester = getProjectTester();
1590 tester.setContinuous(isTrue(getProperty(Constants.TESTCONTINUOUS)));
1591 tester.prepare();
1592
1593 if (!isOk()) {
1594 return;
1595 }
1596 int errors = tester.test();
1597 if (errors == 0) {
1598 System.err.println("No Errors");
1599 } else {
1600 if (errors > 0) {
1601 System.err.println(errors + " Error(s)");
1602
1603 } else
1604 System.err.println("Error " + errors);
1605 }
1606 }
1607
1608 /**
1609 * This methods attempts to turn any jar into a valid jar. If this is a
1610 * bundle with manifest, a manifest is added based on defaults. If it is a
1611 * bundle, but not r4, we try to add the r4 headers.
1612 *
1613 * @param descriptor
1614 * @param in
1615 * @return
1616 * @throws Exception
1617 */
1618 public Jar getValidJar(File f) throws Exception {
1619 Jar jar = new Jar(f);
1620 return getValidJar(jar, f.getAbsolutePath());
1621 }
1622
1623 public Jar getValidJar(URL url) throws Exception {
1624 InputStream in = url.openStream();
1625 try {
1626 Jar jar = new Jar(url.getFile().replace('/', '.'), in, System.currentTimeMillis());
1627 return getValidJar(jar, url.toString());
Stuart McCulloch2286f232012-06-15 13:27:53 +00001628 }
1629 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +00001630 in.close();
1631 }
1632 }
1633
1634 public Jar getValidJar(Jar jar, String id) throws Exception {
1635 Manifest manifest = jar.getManifest();
1636 if (manifest == null) {
1637 trace("Wrapping with all defaults");
1638 Builder b = new Builder(this);
1639 b.addClasspath(jar);
1640 b.setProperty("Bnd-Message", "Wrapped from " + id + "because lacked manifest");
1641 b.setProperty(Constants.EXPORT_PACKAGE, "*");
1642 b.setProperty(Constants.IMPORT_PACKAGE, "*;resolution:=optional");
1643 jar = b.build();
1644 } else if (manifest.getMainAttributes().getValue(Constants.BUNDLE_MANIFESTVERSION) == null) {
1645 trace("Not a release 4 bundle, wrapping with manifest as source");
1646 Builder b = new Builder(this);
1647 b.addClasspath(jar);
1648 b.setProperty(Constants.PRIVATE_PACKAGE, "*");
1649 b.mergeManifest(manifest);
1650 String imprts = manifest.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
1651 if (imprts == null)
1652 imprts = "";
1653 else
1654 imprts += ",";
1655 imprts += "*;resolution=optional";
1656
1657 b.setProperty(Constants.IMPORT_PACKAGE, imprts);
1658 b.setProperty("Bnd-Message", "Wrapped from " + id + "because had incomplete manifest");
1659 jar = b.build();
1660 }
1661 return jar;
1662 }
1663
Stuart McCullochd4826102012-06-26 16:34:24 +00001664 public String _project(@SuppressWarnings("unused") String args[]) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001665 return getBase().getAbsolutePath();
1666 }
1667
1668 /**
1669 * Bump the version of this project. First check the main bnd file. If this
1670 * does not contain a version, check the include files. If they still do not
1671 * contain a version, then check ALL the sub builders. If not, add a version
1672 * to the main bnd file.
1673 *
1674 * @param mask
1675 * the mask for bumping, see {@link Macro#_version(String[])}
1676 * @throws Exception
1677 */
1678 public void bump(String mask) throws Exception {
1679 String pattern = "(Bundle-Version\\s*(:|=)\\s*)(([0-9]+(\\.[0-9]+(\\.[0-9]+)?)?))";
1680 String replace = "$1${version;" + mask + ";$3}";
1681 try {
1682 // First try our main bnd file
1683 if (replace(getPropertiesFile(), pattern, replace))
1684 return;
1685
1686 trace("no version in bnd.bnd");
1687
1688 // Try the included filed in reverse order (last has highest
1689 // priority)
1690 List<File> included = getIncluded();
1691 if (included != null) {
1692 List<File> copy = new ArrayList<File>(included);
1693 Collections.reverse(copy);
1694
1695 for (File file : copy) {
1696 if (replace(file, pattern, replace)) {
1697 trace("replaced version in file %s", file);
1698 return;
1699 }
1700 }
1701 }
1702 trace("no version in included files");
1703
1704 boolean found = false;
1705
1706 // Replace in all sub builders.
1707 for (Builder sub : getSubBuilders()) {
1708 found |= replace(sub.getPropertiesFile(), pattern, replace);
1709 }
1710
1711 if (!found) {
1712 trace("no version in sub builders, add it to bnd.bnd");
1713 String bndfile = IO.collect(getPropertiesFile());
1714 bndfile += "\n# Added by by bump\nBundle-Version: 0.0.0\n";
1715 IO.store(bndfile, getPropertiesFile());
1716 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00001717 }
1718 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +00001719 forceRefresh();
1720 }
1721 }
1722
1723 boolean replace(File f, String pattern, String replacement) throws IOException {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001724 final Macro macro = getReplacer();
1725 Sed sed = new Sed( new Replacer() {
1726 public String process(String line) {
1727 return macro.process(line);
1728 }
1729 }, f);
Stuart McCullochbb014372012-06-07 21:57:32 +00001730 sed.replace(pattern, replacement);
1731 return sed.doIt() > 0;
1732 }
1733
1734 public void bump() throws Exception {
1735 bump(getProperty(BUMPPOLICY, "=+0"));
1736 }
1737
1738 public void action(String command) throws Throwable {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001739 Map<String,Action> actions = getActions();
Stuart McCullochbb014372012-06-07 21:57:32 +00001740
1741 Action a = actions.get(command);
1742 if (a == null)
1743 a = new ReflectAction(command);
1744
1745 before(this, command);
1746 try {
1747 a.execute(this, command);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001748 }
1749 catch (Throwable t) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001750 after(this, command, t);
1751 throw t;
1752 }
1753 }
1754
1755 /**
1756 * Run all before command plugins
Stuart McCullochbb014372012-06-07 21:57:32 +00001757 */
Stuart McCullochd4826102012-06-26 16:34:24 +00001758 void before(@SuppressWarnings("unused") Project p, String a) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001759 List<CommandPlugin> testPlugins = getPlugins(CommandPlugin.class);
1760 for (CommandPlugin testPlugin : testPlugins) {
1761 testPlugin.before(this, a);
1762 }
1763 }
1764
1765 /**
1766 * Run all after command plugins
1767 */
Stuart McCullochd4826102012-06-26 16:34:24 +00001768 void after(@SuppressWarnings("unused") Project p, String a, Throwable t) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001769 List<CommandPlugin> testPlugins = getPlugins(CommandPlugin.class);
1770 for (int i = testPlugins.size() - 1; i >= 0; i--) {
1771 testPlugins.get(i).after(this, a, t);
1772 }
1773 }
1774
1775 public String _findfile(String args[]) {
1776 File f = getFile(args[1]);
1777 List<String> files = new ArrayList<String>();
1778 tree(files, f, "", new Instruction(args[2]));
1779 return join(files);
1780 }
1781
1782 void tree(List<String> list, File current, String path, Instruction instr) {
1783 if (path.length() > 0)
1784 path = path + "/";
1785
1786 String subs[] = current.list();
1787 if (subs != null) {
1788 for (String sub : subs) {
1789 File f = new File(current, sub);
1790 if (f.isFile()) {
1791 if (instr.matches(sub) && !instr.isNegated())
1792 list.add(path + sub);
1793 } else
1794 tree(list, f, path + sub, instr);
1795 }
1796 }
1797 }
1798
1799 public void refreshAll() {
1800 workspace.refresh();
1801 refresh();
1802 }
1803
Stuart McCulloch2286f232012-06-15 13:27:53 +00001804 @SuppressWarnings("unchecked")
Stuart McCullochd4826102012-06-26 16:34:24 +00001805 public void script(@SuppressWarnings("unused") String type, String script) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +00001806 // TODO check tyiping
1807 List<Scripter> scripters = getPlugins(Scripter.class);
1808 if (scripters.isEmpty()) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001809 msgs.NoScripters_(script);
Stuart McCullochbb014372012-06-07 21:57:32 +00001810 return;
1811 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00001812 @SuppressWarnings("rawtypes")
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001813 Map x = getProperties();
1814 scripters.get(0).eval(x, new StringReader(script));
Stuart McCullochbb014372012-06-07 21:57:32 +00001815 }
1816
Stuart McCullochd4826102012-06-26 16:34:24 +00001817 public String _repos(@SuppressWarnings("unused") String args[]) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +00001818 List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
1819 List<String> names = new ArrayList<String>();
1820 for (RepositoryPlugin rp : repos)
1821 names.add(rp.getName());
1822 return join(names, ", ");
1823 }
1824
1825 public String _help(String args[]) throws Exception {
1826 if (args.length == 1)
1827 return "Specify the option or header you want information for";
1828
1829 Syntax syntax = Syntax.HELP.get(args[1]);
1830 if (syntax == null)
1831 return "No help for " + args[1];
1832
1833 String what = null;
1834 if (args.length > 2)
1835 what = args[2];
1836
1837 if (what == null || what.equals("lead"))
1838 return syntax.getLead();
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001839 if (what.equals("example"))
Stuart McCullochbb014372012-06-07 21:57:32 +00001840 return syntax.getExample();
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001841 if (what.equals("pattern"))
Stuart McCullochbb014372012-06-07 21:57:32 +00001842 return syntax.getPattern();
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00001843 if (what.equals("values"))
Stuart McCullochbb014372012-06-07 21:57:32 +00001844 return syntax.getValues();
1845
1846 return "Invalid type specified for help: lead, example, pattern, values";
1847 }
1848
1849 /**
1850 * Returns containers for the deliverables of this project. The deliverables
1851 * is the project builder for this project if no -sub is specified.
1852 * Otherwise it contains all the sub bnd files.
1853 *
1854 * @return A collection of containers
Stuart McCullochbb014372012-06-07 21:57:32 +00001855 * @throws Exception
1856 */
1857 public Collection<Container> getDeliverables() throws Exception {
1858 List<Container> result = new ArrayList<Container>();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001859 Collection< ? extends Builder> builders = getSubBuilders();
Stuart McCullochbb014372012-06-07 21:57:32 +00001860
1861 for (Builder builder : builders) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001862 Container c = new Container(this, builder.getBsn(), builder.getVersion(), Container.TYPE.PROJECT,
1863 getOutputFile(builder.getBsn()), null, null);
Stuart McCullochbb014372012-06-07 21:57:32 +00001864 result.add(c);
1865 }
1866 return result;
1867
1868 }
1869
1870 /**
1871 * Return the builder associated with the give bnd file or null. The bnd.bnd
1872 * file can contain -sub option. This option allows specifying files in the
1873 * same directory that should drive the generation of multiple deliverables.
1874 * This method figures out if the bndFile is actually one of the bnd files
1875 * of a deliverable.
1876 *
1877 * @param bndFile
1878 * A file pointing to a bnd file.
1879 * @return null or the builder for a sub file.
1880 * @throws Exception
1881 */
1882 public Builder getSubBuilder(File bndFile) throws Exception {
1883 bndFile = bndFile.getCanonicalFile();
1884
1885 // Verify that we are inside the project.
1886 File base = getBase().getCanonicalFile();
1887 if (!bndFile.getAbsolutePath().startsWith(base.getAbsolutePath()))
1888 return null;
1889
Stuart McCulloch2286f232012-06-15 13:27:53 +00001890 Collection< ? extends Builder> builders = getSubBuilders();
Stuart McCullochbb014372012-06-07 21:57:32 +00001891 for (Builder sub : builders) {
1892 File propertiesFile = sub.getPropertiesFile();
1893 if (propertiesFile != null) {
1894 if (propertiesFile.getCanonicalFile().equals(bndFile)) {
1895 // Found it!
1896 return sub;
1897 }
1898 }
1899 }
1900 return null;
1901 }
1902
1903 /**
1904 * Answer the container associated with a given bsn.
1905 *
1906 * @param bndFile
1907 * A file pointing to a bnd file.
1908 * @return null or the builder for a sub file.
1909 * @throws Exception
1910 */
Stuart McCullochd4826102012-06-26 16:34:24 +00001911 public Container getDeliverable(String bsn, @SuppressWarnings("unused") Map<String,String> attrs) throws Exception {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001912 Collection< ? extends Builder> builders = getSubBuilders();
Stuart McCullochbb014372012-06-07 21:57:32 +00001913 for (Builder sub : builders) {
1914 if (sub.getBsn().equals(bsn))
1915 return new Container(this, getOutputFile(bsn));
1916 }
1917 return null;
1918 }
1919
1920 /**
1921 * Get a list of the sub builders. A bnd.bnd file can contain the -sub
1922 * option. This will generate multiple deliverables. This method returns the
1923 * builders for each sub file. If no -sub option is present, the list will
1924 * contain a builder for the bnd.bnd file.
1925 *
1926 * @return A list of builders.
1927 * @throws Exception
1928 */
Stuart McCulloch2286f232012-06-15 13:27:53 +00001929 public Collection< ? extends Builder> getSubBuilders() throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +00001930 return getBuilder(null).getSubBuilders();
1931 }
1932
1933 /**
1934 * Calculate the classpath. We include our own runtime.jar which includes
1935 * the test framework and we include the first of the test frameworks
1936 * specified.
1937 *
1938 * @throws Exception
1939 */
1940 Collection<File> toFile(Collection<Container> containers) throws Exception {
1941 ArrayList<File> files = new ArrayList<File>();
1942 for (Container container : containers) {
1943 container.contributeFiles(files, this);
1944 }
1945 return files;
1946 }
1947
1948 public Collection<String> getRunVM() {
1949 Parameters hdr = getParameters(RUNVM);
1950 return hdr.keySet();
1951 }
1952
Stuart McCulloch2286f232012-06-15 13:27:53 +00001953 public Map<String,String> getRunProperties() {
Stuart McCullochbb014372012-06-07 21:57:32 +00001954 return OSGiHeader.parseProperties(getProperty(RUNPROPERTIES));
1955 }
1956
1957 /**
1958 * Get a launcher.
1959 *
1960 * @return
1961 * @throws Exception
1962 */
1963 public ProjectLauncher getProjectLauncher() throws Exception {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001964 return getHandler(ProjectLauncher.class, getRunpath(), LAUNCHER_PLUGIN, "biz.aQute.launcher");
Stuart McCullochbb014372012-06-07 21:57:32 +00001965 }
1966
1967 public ProjectTester getProjectTester() throws Exception {
1968 return getHandler(ProjectTester.class, getTestpath(), TESTER_PLUGIN, "biz.aQute.junit");
1969 }
1970
Stuart McCulloch2286f232012-06-15 13:27:53 +00001971 private <T> T getHandler(Class<T> target, Collection<Container> containers, String header, String defaultHandler)
1972 throws Exception {
1973 Class< ? extends T> handlerClass = target;
Stuart McCullochbb014372012-06-07 21:57:32 +00001974
1975 // Make sure we find at least one handler, but hope to find an earlier
1976 // one
1977 List<Container> withDefault = Create.list();
1978 withDefault.addAll(containers);
1979 withDefault.addAll(getBundles(Strategy.HIGHEST, defaultHandler, null));
1980 trace("candidates for tester %s", withDefault);
1981
1982 for (Container c : withDefault) {
1983 Manifest manifest = c.getManifest();
1984
1985 if (manifest != null) {
1986 String launcher = manifest.getMainAttributes().getValue(header);
1987 if (launcher != null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001988 Class< ? > clz = getClass(launcher, c.getFile());
Stuart McCullochbb014372012-06-07 21:57:32 +00001989 if (clz != null) {
1990 if (!target.isAssignableFrom(clz)) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001991 msgs.IncompatibleHandler_For_(launcher, defaultHandler);
Stuart McCullochbb014372012-06-07 21:57:32 +00001992 } else {
1993 handlerClass = clz.asSubclass(target);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001994 Constructor< ? extends T> constructor = handlerClass.getConstructor(Project.class);
Stuart McCullochbb014372012-06-07 21:57:32 +00001995 return constructor.newInstance(this);
1996 }
1997 }
1998 }
1999 }
2000 }
2001
Stuart McCulloch2286f232012-06-15 13:27:53 +00002002 throw new IllegalArgumentException("Default handler for " + header + " not found in " + defaultHandler);
Stuart McCullochbb014372012-06-07 21:57:32 +00002003 }
2004
2005 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +00002006 * Make this project delay the calculation of the run dependencies. The run
2007 * dependencies calculation can be done in prepare or until the dependencies
2008 * are actually needed.
Stuart McCullochbb014372012-06-07 21:57:32 +00002009 */
2010 public void setDelayRunDependencies(boolean x) {
2011 delayRunDependencies = x;
2012 }
2013
2014 /**
2015 * Sets the package version on an exported package
2016 *
2017 * @param packageName
2018 * The package name
2019 * @param version
2020 * The new package version
2021 */
2022 public void setPackageInfo(String packageName, Version version) {
2023 try {
2024 updatePackageInfoFile(packageName, version);
Stuart McCulloch2286f232012-06-15 13:27:53 +00002025 }
2026 catch (Exception e) {
2027 msgs.SettingPackageInfoException_(e);
Stuart McCullochbb014372012-06-07 21:57:32 +00002028 }
2029 }
2030
2031 void updatePackageInfoFile(String packageName, Version newVersion) throws Exception {
2032
2033 File file = getPackageInfoFile(packageName);
2034
2035 // If package/classes are copied into the bundle through Private-Package
2036 // etc, there will be no source
2037 if (!file.getParentFile().exists()) {
2038 return;
2039 }
2040
2041 Version oldVersion = getPackageInfo(packageName);
2042
2043 if (newVersion.compareTo(oldVersion) == 0) {
2044 return;
Stuart McCullochbb014372012-06-07 21:57:32 +00002045 }
Stuart McCullochd4826102012-06-26 16:34:24 +00002046 PrintWriter pw = IO.writer(file);
2047 pw.println("version " + newVersion);
2048 pw.flush();
2049 pw.close();
2050
2051 String path = packageName.replace('.', '/') + "/packageinfo";
2052 File binary = IO.getFile(getOutput(), path);
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00002053 File bp = binary.getParentFile();
2054 if (!bp.exists() && !bp.mkdirs()) {
2055 throw new IOException("Could not create directory " + bp);
2056 }
Stuart McCullochd4826102012-06-26 16:34:24 +00002057 IO.copy(file, binary);
2058
2059 refresh();
Stuart McCullochbb014372012-06-07 21:57:32 +00002060 }
2061
Stuart McCullochffa8aaf2012-06-17 20:38:35 +00002062 File getPackageInfoFile(String packageName) {
Stuart McCullochbb014372012-06-07 21:57:32 +00002063 String path = packageName.replace('.', '/') + "/packageinfo";
2064 return IO.getFile(getSrc(), path);
2065
2066 }
2067
2068 public Version getPackageInfo(String packageName) throws IOException {
2069 File packageInfoFile = getPackageInfoFile(packageName);
2070 if (!packageInfoFile.exists()) {
2071 return Version.emptyVersion;
2072 }
2073 BufferedReader reader = null;
2074 try {
2075 reader = IO.reader(packageInfoFile);
2076 String line;
2077 while ((line = reader.readLine()) != null) {
2078 line = line.trim();
2079 if (line.startsWith("version ")) {
2080 return Version.parseVersion(line.substring(8));
2081 }
2082 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00002083 }
2084 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +00002085 if (reader != null) {
2086 IO.close(reader);
2087 }
2088 }
2089 return Version.emptyVersion;
2090 }
2091
2092 /**
2093 * bnd maintains a class path that is set by the environment, i.e. bnd is
2094 * not in charge of it.
2095 */
Stuart McCulloch2286f232012-06-15 13:27:53 +00002096
2097 public void addClasspath(File f) {
2098 if (!f.isFile() && !f.isDirectory()) {
2099 msgs.AddingNonExistentFileToClassPath_(f);
Stuart McCullochbb014372012-06-07 21:57:32 +00002100 }
2101 Container container = new Container(f);
2102 classpath.add(container);
2103 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00002104
Stuart McCullochbb014372012-06-07 21:57:32 +00002105 public void clearClasspath() {
2106 classpath.clear();
2107 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00002108
Stuart McCullochbb014372012-06-07 21:57:32 +00002109 public Collection<Container> getClasspath() {
2110 return classpath;
2111 }
2112}