aQute.bnd.build.Project Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bndlib Show documentation
Show all versions of bndlib Show documentation
The bndlib project is a general library to be used with OSGi bundles. It contains
lots of cool functionality that calculates dependencies, etc.
package aQute.bnd.build;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.locks.*;
import java.util.jar.*;
import java.util.regex.*;
import aQute.bnd.header.*;
import aQute.bnd.help.*;
import aQute.bnd.maven.support.*;
import aQute.bnd.osgi.*;
import aQute.bnd.osgi.eclipse.*;
import aQute.bnd.service.*;
import aQute.bnd.service.RepositoryPlugin.PutResult;
import aQute.bnd.service.action.*;
import aQute.bnd.version.*;
import aQute.lib.io.*;
import aQute.libg.generics.*;
import aQute.libg.reporter.*;
import aQute.libg.sed.*;
/**
* This class is NOT threadsafe
*/
public class Project extends Processor {
final static Pattern VERSION_ANNOTATION = Pattern.compile("@\\s*(:?aQute\\.bnd\\.annotation\\.)?Version\\s*\\(\\s*(:?value\\s*=\\s*)?\"(\\d+(:?\\.\\d+(:?\\.\\d+(:?\\.[\\d\\w-_]+)?)?)?)\"\\s*\\)");
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";
public final static String BNDFILE = "bnd.bnd";
public final static String BNDCNF = "cnf";
final Workspace workspace;
boolean preparedPaths;
final Collection dependson = new LinkedHashSet();
final Collection classpath = new LinkedHashSet();
final Collection buildpath = new LinkedHashSet();
final Collection testpath = new LinkedHashSet();
final Collection runpath = new LinkedHashSet();
final Collection runbundles = new LinkedHashSet();
File runstorage;
final Collection sourcepath = new LinkedHashSet();
final Collection allsourcepath = new LinkedHashSet();
final Collection bootclasspath = new LinkedHashSet();
final Lock lock = new ReentrantLock(true);
volatile String lockingReason;
volatile Thread lockingThread;
File output;
File target;
boolean inPrepare;
int revision;
File files[];
static List trail = new ArrayList();
boolean delayRunDependencies = false;
final ProjectMessages msgs = ReporterMessages.base(this, ProjectMessages.class);
public Project(Workspace workspace, File projectDir, File buildFile) throws Exception {
super(workspace);
this.workspace = workspace;
setFileMustExist(false);
setProperties(buildFile);
assert workspace != null;
// For backward compatibility reasons, we also read
readBuildProperties();
}
public Project(Workspace workspace, File buildDir) throws Exception {
this(workspace, buildDir, new File(buildDir, BNDFILE));
}
private void readBuildProperties() throws Exception {
try {
File f = getFile("build.properties");
if (f.isFile()) {
Properties p = loadProperties(f);
for (Enumeration< ? > e = p.propertyNames(); e.hasMoreElements();) {
String key = (String) e.nextElement();
String newkey = key;
if (key.indexOf('$') >= 0) {
newkey = getReplacer().process(key);
}
setProperty(newkey, p.getProperty(key));
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
public static Project getUnparented(File propertiesFile) throws Exception {
propertiesFile = propertiesFile.getAbsoluteFile();
Workspace workspace = new Workspace(propertiesFile.getParentFile());
Project project = new Project(workspace, propertiesFile.getParentFile());
project.setProperties(propertiesFile);
project.setFileMustExist(true);
return project;
}
public synchronized boolean isValid() {
return getBase().isDirectory() && getPropertiesFile().isFile();
}
/**
* Return a new builder that is nicely setup for this project. Please close
* this builder after use.
*
* @param parent
* The project builder to use as parent, use this project if null
* @return
* @throws Exception
*/
public synchronized ProjectBuilder getBuilder(ProjectBuilder parent) throws Exception {
ProjectBuilder builder;
if (parent == null)
builder = new ProjectBuilder(this);
else
builder = new ProjectBuilder(parent);
builder.setBase(getBase());
builder.setPedantic(isPedantic());
builder.setTrace(isTrace());
return builder;
}
public synchronized int getChanged() {
return revision;
}
/*
* Indicate a change in the external world that affects our build. This will
* clear any cached results.
*/
public synchronized void setChanged() {
// if (refresh()) {
preparedPaths = false;
files = null;
revision++;
// }
}
public Workspace getWorkspace() {
return workspace;
}
@Override
public String toString() {
return getBase().getName();
}
/**
* Set up all the paths
*/
public synchronized void prepare() throws Exception {
if (!isValid()) {
warning("Invalid project attempts to prepare: %s", this);
return;
}
if (inPrepare)
throw new CircularDependencyException(trail.toString() + "," + this);
trail.add(this);
try {
if (!preparedPaths) {
inPrepare = true;
try {
dependson.clear();
buildpath.clear();
sourcepath.clear();
allsourcepath.clear();
bootclasspath.clear();
testpath.clear();
runpath.clear();
runbundles.clear();
// We use a builder to construct all the properties for
// use.
setProperty("basedir", getBase().getAbsolutePath());
// If a bnd.bnd file exists, we read it.
// Otherwise, we just do the build properties.
if (!getPropertiesFile().isFile() && new File(getBase(), ".classpath").isFile()) {
// Get our Eclipse info, we might depend on other
// projects
// though ideally this should become empty and void
doEclipseClasspath();
}
// Calculate our source directory
File src = getSrc();
if (src.isDirectory()) {
sourcepath.add(src);
allsourcepath.add(src);
} else
sourcepath.add(getBase());
// Set default bin directory
output = getOutput0();
if (!output.exists()) {
if (!output.mkdirs()) {
throw new IOException("Could not create directory " + output);
}
getWorkspace().changedFile(output);
}
if (!output.isDirectory())
msgs.NoOutputDirectory_(output);
else {
Container c = new Container(this, output);
if (!buildpath.contains(c))
buildpath.add(c);
}
// Where we store all our generated stuff.
target = getTarget0();
// Where the launched OSGi framework stores stuff
String runStorageStr = getProperty(Constants.RUNSTORAGE);
runstorage = runStorageStr != null ? getFile(runStorageStr) : null;
// We might have some other projects we want build
// before we do anything, but these projects are not in
// our path. The -dependson allows you to build them before.
// The values are possibly negated globbing patterns.
// dependencies.add( getWorkspace().getProject("cnf"));
String dp = getProperty(Constants.DEPENDSON);
Set requiredProjectNames = new LinkedHashSet(new Parameters(dp).keySet());
//Allow DependencyConstributors to modify requiredProjectNames
List dcs = getPlugins(DependencyContributor.class);
for (DependencyContributor dc : dcs)
dc.addDependencies(this, requiredProjectNames);
Instructions is = new Instructions(requiredProjectNames);
Set unused = new HashSet();
Collection projects = getWorkspace().getAllProjects();
Collection dependencies = is.select(projects, unused, false);
for (Instruction u: unused)
msgs.MissingDependson_(u.getInput());
// We have two paths that consists of repo files, projects,
// or some other stuff. The doPath routine adds them to the
// path and extracts the projects so we can build them
// before.
doPath(buildpath, dependencies, parseBuildpath(), bootclasspath);
doPath(testpath, dependencies, parseTestpath(), bootclasspath);
if (!delayRunDependencies) {
doPath(runpath, dependencies, parseRunpath(), null);
doPath(runbundles, dependencies, parseRunbundles(), null);
}
// We now know all dependent projects. But we also depend
// on whatever those projects depend on. This creates an
// ordered list without any duplicates. This of course
// assumes
// that there is no circularity. However, this is checked
// by the inPrepare flag, will throw an exception if we
// are circular.
Set done = new HashSet();
done.add(this);
allsourcepath.addAll(sourcepath);
for (Project project : dependencies)
project.traverse(dependson, done);
for (Project project : dependson) {
allsourcepath.addAll(project.getSourcePath());
}
if (isOk())
preparedPaths = true;
}
finally {
inPrepare = false;
}
}
}
finally {
trail.remove(this);
}
}
/**
* @return
*/
private File getOutput0() {
return getFile(getProperty("bin", "bin")).getAbsoluteFile();
}
/**
*
*/
private File getTarget0() throws IOException {
File target = getFile(getProperty("target", "generated"));
if (!target.exists()) {
if (!target.mkdirs()) {
throw new IOException("Could not create directory " + target);
}
getWorkspace().changedFile(target);
}
return target;
}
public File getSrc() {
return new File(getBase(), getProperty("src", "src"));
}
private void traverse(Collection dependencies, Set visited) throws Exception {
if (visited.contains(this))
return;
visited.add(this);
for (Project project : getDependson())
project.traverse(dependencies, visited);
dependencies.add(this);
}
/**
* Iterate over the entries and place the projects on the projects list and
* all the files of the entries on the resultpath.
*
* @param resultpath
* The list that gets all the files
* @param projects
* The list that gets any projects that are entries
* @param entries
* The input list of classpath entries
*/
private void doPath(Collection resultpath, Collection projects, Collection entries,
Collection bootclasspath) {
for (Container cpe : entries) {
if (cpe.getError() != null)
error(cpe.getError());
else {
if (cpe.getType() == Container.TYPE.PROJECT) {
projects.add(cpe.getProject());
}
if (bootclasspath != null
&& (cpe.getBundleSymbolicName().startsWith("ee.") || cpe.getAttributes().containsKey("boot")))
bootclasspath.add(cpe);
else
resultpath.add(cpe);
}
}
}
/**
* Parse the list of bundles that are a prerequisite to this project.
* Bundles are listed in repo specific names. So we just let our repo
* plugins iterate over the list of bundles and we get the highest version
* from them.
*
* @return
*/
private List parseBuildpath() throws Exception {
List bundles = getBundles(Strategy.LOWEST, getProperty(Constants.BUILDPATH), Constants.BUILDPATH);
return bundles;
}
private List parseRunpath() throws Exception {
return getBundles(Strategy.HIGHEST, getProperty(Constants.RUNPATH), Constants.RUNPATH);
}
private List parseRunbundles() throws Exception {
return getBundles(Strategy.HIGHEST, getProperty(Constants.RUNBUNDLES), Constants.RUNBUNDLES);
}
private List parseTestpath() throws Exception {
return getBundles(Strategy.HIGHEST, getProperty(Constants.TESTPATH), Constants.TESTPATH);
}
/**
* Analyze the header and return a list of files that should be on the
* build, test or some other path. The list is assumed to be a list of bsns
* with a version specification. The special case of version=project
* indicates there is a project in the same workspace. The path to the
* output directory is calculated. The default directory ${bin} can be
* overridden with the output attribute.
*
* @param strategy
* STRATEGY_LOWEST or STRATEGY_HIGHEST
* @param spec
* The header
* @return
*/
public List getBundles(Strategy strategyx, String spec, String source) throws Exception {
List result = new ArrayList();
Parameters bundles = new Parameters(spec);
try {
for (Iterator> i = bundles.entrySet().iterator(); i.hasNext();) {
Entry entry = i.next();
String bsn = removeDuplicateMarker(entry.getKey());
Map attrs = entry.getValue();
Container found = null;
String versionRange = attrs.get("version");
if (versionRange != null) {
if (versionRange.equals("latest") || versionRange.equals("snapshot")) {
found = getBundle(bsn, versionRange, strategyx, attrs);
}
}
if (found == null) {
if (versionRange != null && (versionRange.equals("project") || versionRange.equals("latest"))) {
Project project = getWorkspace().getProject(bsn);
if (project != null && project.exists()) {
File f = project.getOutput();
found = new Container(project, bsn, versionRange, Container.TYPE.PROJECT, f, null, attrs, null);
} else {
msgs.NoSuchProject(bsn, spec);
continue;
}
} else if (versionRange != null && versionRange.equals("file")) {
File f = getFile(bsn);
String error = null;
if (!f.exists())
error = "File does not exist: " + f.getAbsolutePath();
if (f.getName().endsWith(".lib")) {
found = new Container(this, bsn, "file", Container.TYPE.LIBRARY, f, error, attrs, null);
} else {
found = new Container(this, bsn, "file", Container.TYPE.EXTERNAL, f, error, attrs, null);
}
} else {
found = getBundle(bsn, versionRange, strategyx, attrs);
}
}
if (found != null) {
List libs = found.getMembers();
for (Container cc : libs) {
if (result.contains(cc))
warning("Multiple bundles with the same final URL: %s, dropped duplicate", cc);
else
result.add(cc);
}
} else {
// Oops, not a bundle in sight :-(
Container x = new Container(this, bsn, versionRange, Container.TYPE.ERROR, null, bsn + ";version="
+ versionRange + " not found", attrs, null);
result.add(x);
warning("Can not find URL for bsn " + bsn);
}
}
}
catch (CircularDependencyException e) {
String message = e.getMessage();
if (source != null)
message = String.format("%s (from property: %s)", message, source);
msgs.CircularDependencyContext_Message_(getName(), message);
}
catch (Exception e) {
msgs.Unexpected_Error_(spec, e);
}
return result;
}
/**
* Just calls a new method with a default parm.
*
* @throws Exception
*/
Collection getBundles(Strategy strategy, String spec) throws Exception {
return getBundles(strategy, spec, null);
}
static void mergeNames(String names, Set set) {
StringTokenizer tokenizer = new StringTokenizer(names, ",");
while (tokenizer.hasMoreTokens())
set.add(tokenizer.nextToken().trim());
}
static String flatten(Set names) {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (String name : names) {
if (!first)
builder.append(',');
builder.append(name);
first = false;
}
return builder.toString();
}
static void addToPackageList(Container container, String newPackageNames) {
Set merged = new HashSet();
String packageListStr = container.attributes.get("packages");
if (packageListStr != null)
mergeNames(packageListStr, merged);
if (newPackageNames != null)
mergeNames(newPackageNames, merged);
container.putAttribute("packages", flatten(merged));
}
/**
* The user selected pom in a path. This will place the pom as well as its
* dependencies on the list
*
* @param strategyx
* the strategy to use.
* @param result
* The list of result containers
* @param attrs
* The attributes
* @throws Exception
* anything goes wrong
*/
public void doMavenPom(Strategy strategyx, List result, String action) throws Exception {
File pomFile = getFile("pom.xml");
if (!pomFile.isFile())
msgs.MissingPom();
else {
ProjectPom pom = getWorkspace().getMaven().createProjectModel(pomFile);
if (action == null)
action = "compile";
Pom.Scope act = Pom.Scope.valueOf(action);
Set dependencies = pom.getDependencies(act);
for (Pom sub : dependencies) {
File artifact = sub.getArtifact();
Container container = new Container(artifact, null);
result.add(container);
}
}
}
public Collection getDependson() throws Exception {
prepare();
return dependson;
}
public Collection getBuildpath() throws Exception {
prepare();
return buildpath;
}
public Collection getTestpath() throws Exception {
prepare();
return testpath;
}
/**
* Handle dependencies for paths that are calculated on demand.
*
* @param testpath2
* @param parseTestpath
*/
private void justInTime(Collection path, List entries) {
if (delayRunDependencies && path.isEmpty())
doPath(path, dependson, entries, null);
}
public Collection getRunpath() throws Exception {
prepare();
justInTime(runpath, parseRunpath());
return runpath;
}
public Collection getRunbundles() throws Exception {
prepare();
justInTime(runbundles, parseRunbundles());
return runbundles;
}
public File getRunStorage() throws Exception {
prepare();
return runstorage;
}
public boolean getRunBuilds() {
boolean result;
String runBuildsStr = getProperty(Constants.RUNBUILDS);
if (runBuildsStr == null)
result = !getPropertiesFile().getName().toLowerCase().endsWith(Constants.DEFAULT_BNDRUN_EXTENSION);
else
result = Boolean.parseBoolean(runBuildsStr);
return result;
}
public Collection getSourcePath() throws Exception {
prepare();
return sourcepath;
}
public Collection getAllsourcepath() throws Exception {
prepare();
return allsourcepath;
}
public Collection getBootclasspath() throws Exception {
prepare();
return bootclasspath;
}
public File getOutput() throws Exception {
prepare();
return output;
}
private void doEclipseClasspath() throws Exception {
EclipseClasspath eclipse = new EclipseClasspath(this, getWorkspace().getBase(), getBase());
eclipse.setRecurse(false);
// We get the file directories but in this case we need
// to tell ant that the project names
for (File dependent : eclipse.getDependents()) {
Project required = workspace.getProject(dependent.getName());
dependson.add(required);
}
for (File f : eclipse.getClasspath()) {
buildpath.add(new Container(f, null));
}
for (File f : eclipse.getBootclasspath()) {
bootclasspath.add(new Container(f, null));
}
sourcepath.addAll(eclipse.getSourcepath());
allsourcepath.addAll(eclipse.getAllSources());
output = eclipse.getOutput();
}
public String _p_dependson(String args[]) throws Exception {
return list(args, toFiles(getDependson()));
}
private Collection< ? > toFiles(Collection projects) {
List files = new ArrayList();
for (Project p : projects) {
files.add(p.getBase());
}
return files;
}
public String _p_buildpath(String args[]) throws Exception {
return list(args, getBuildpath());
}
public String _p_testpath(String args[]) throws Exception {
return list(args, getRunpath());
}
public String _p_sourcepath(String args[]) throws Exception {
return list(args, getSourcePath());
}
public String _p_allsourcepath(String args[]) throws Exception {
return list(args, getAllsourcepath());
}
public String _p_bootclasspath(String args[]) throws Exception {
return list(args, getBootclasspath());
}
public String _p_output(String args[]) throws Exception {
if (args.length != 1)
throw new IllegalArgumentException("${output} should not have arguments");
return getOutput().getAbsolutePath();
}
private String list(String[] args, Collection< ? > list) {
if (args.length > 3)
throw new IllegalArgumentException("${" + args[0]
+ "[;]} can only take a separator as argument, has " + Arrays.toString(args));
String separator = ",";
if (args.length == 2) {
separator = args[1];
}
return join(list, separator);
}
@Override
protected Object[] getMacroDomains() {
return new Object[] {
workspace
};
}
public File release(String jarName, InputStream jarStream) throws Exception {
String name = getProperty(Constants.RELEASEREPO);
return release(name, jarName, jarStream);
}
public URI releaseURI(String jarName, InputStream jarStream) throws Exception {
String name = getProperty(Constants.RELEASEREPO);
return releaseURI(name, jarName, jarStream);
}
/**
* Release
*
* @param name
* The repository name
* @param jarName
* @param jarStream
* @return
* @throws Exception
*/
public File release(String name, String jarName, InputStream jarStream) throws Exception {
URI uri = releaseURI(name, jarName, jarStream);
if (uri != null) {
return new File(uri);
}
return null;
}
public URI releaseURI(String name, String jarName, InputStream jarStream) throws Exception {
trace("release to %s", name);
RepositoryPlugin repo = getReleaseRepo(name);
if (repo == null) {
if (name == null)
msgs.NoNameForReleaseRepository();
else
msgs.ReleaseRepository_NotFoundIn_(name, getPlugins(RepositoryPlugin.class));
return null;
}
try {
PutResult r = repo.put(jarStream, new RepositoryPlugin.PutOptions());
trace("Released %s to %s in repository %s", jarName, r.artifact, repo);
return r.artifact;
}
catch (Exception e) {
msgs.Release_Into_Exception_(jarName, repo, e);
return null;
}
}
RepositoryPlugin getReleaseRepo(String releaserepo) {
String name = releaserepo == null ?
name = getProperty(RELEASEREPO) : releaserepo;
List plugins = getPlugins(RepositoryPlugin.class);
for (RepositoryPlugin plugin : plugins) {
if (!plugin.canWrite())
continue;
if (name == null)
return plugin;
if (name.equals(plugin.getName()))
return plugin;
}
return null;
}
public void release(boolean test) throws Exception {
String name = getProperty(Constants.RELEASEREPO);
release(name, test);
}
/**
* Release
*
* @param name
* The respository name
* @param test
* Run testcases
* @throws Exception
*/
public void release(String name, boolean test) throws Exception {
trace("release");
File[] jars = build(test);
// If build fails jars will be null
if (jars == null) {
trace("no jars being build");
return;
}
trace("build ", Arrays.toString(jars));
for (File jar : jars) {
release(name, jar.getName(), new BufferedInputStream(new FileInputStream(jar)));
}
}
/**
* Get a bundle from one of the plugin repositories. If an exact version is
* required we just return the first repository found (in declaration order
* in the build.bnd file).
*
* @param bsn
* The bundle symbolic name
* @param range
* The version range
* @param lowest
* set to LOWEST or HIGHEST
* @return the file object that points to the bundle or null if not found
* @throws Exception
* when something goes wrong
*/
public Container getBundle(String bsn, String range, Strategy strategy, Map attrs) throws Exception {
if (range == null)
range = "0";
if ("snapshot".equals(range)) {
return getBundleFromProject(bsn, attrs);
}
Strategy useStrategy = strategy;
if ("latest".equals(range)) {
Container c = getBundleFromProject(bsn, attrs);
if (c != null)
return c;
useStrategy = Strategy.HIGHEST;
}
useStrategy = overrideStrategy(attrs, useStrategy);
List plugins = workspace.getRepositories();
if (useStrategy == Strategy.EXACT) {
if (!Verifier.isVersion(range))
return new Container(this, bsn, range, Container.TYPE.ERROR, null, bsn + ";version=" + range
+ " Invalid version", null, null);
// For an exact range we just iterate over the repos
// and return the first we find.
Version version = new Version(range);
for (RepositoryPlugin plugin : plugins) {
DownloadBlocker blocker = new DownloadBlocker(this);
File result = plugin.get(bsn, version, attrs, blocker);
if (result != null)
return toContainer(bsn, range, attrs, result, blocker);
}
} else {
VersionRange versionRange = "latest".equals(range) ? new VersionRange("0") : new VersionRange(range);
// We have a range search. Gather all the versions in all the repos
// and make a decision on that choice. If the same version is found
// in
// multiple repos we take the first
SortedMap versions = new TreeMap();
for (RepositoryPlugin plugin : plugins) {
try {
SortedSet vs = plugin.versions(bsn);
if (vs != null) {
for (Version v : vs) {
if (!versions.containsKey(v) && versionRange.includes(v))
versions.put(v, plugin);
}
}
}
catch (UnsupportedOperationException ose) {
// We have a plugin that cannot list versions, try
// if it has this specific version
// The main reaosn for this code was the Maven Remote
// Repository
// To query, we must have a real version
if (!versions.isEmpty() && Verifier.isVersion(range)) {
Version version = new Version(range);
DownloadBlocker blocker = new DownloadBlocker(this);
File file = plugin.get(bsn, version, attrs, blocker);
// and the entry must exist
// if it does, return this as a result
if (file != null)
return toContainer(bsn, range, attrs, file, blocker);
}
}
}
// Verify if we found any, if so, we use the strategy to pick
// the first or last
if (!versions.isEmpty()) {
Version provider = null;
switch (useStrategy) {
case HIGHEST :
provider = versions.lastKey();
break;
case LOWEST :
provider = versions.firstKey();
break;
case EXACT :
// TODO need to handle exact better
break;
}
if (provider != null) {
RepositoryPlugin repo = versions.get(provider);
String version = provider.toString();
DownloadBlocker blocker = new DownloadBlocker(this);
File result = repo.get(bsn, provider, attrs, blocker);
if (result != null)
return toContainer(bsn, version, attrs, result, blocker);
} else
msgs.FoundVersions_ForStrategy_ButNoProvider(versions, useStrategy);
}
}
//
// If we get this far we ran into an error somewhere
return new Container(this, bsn, range, Container.TYPE.ERROR, null, bsn + ";version=" + range + " Not found in "
+ plugins, null, null);
}
/**
* @param attrs
* @param useStrategy
* @return
*/
protected Strategy overrideStrategy(Map attrs, Strategy useStrategy) {
if (attrs != null) {
String overrideStrategy = attrs.get("strategy");
if (overrideStrategy != null) {
if ("highest".equalsIgnoreCase(overrideStrategy))
useStrategy = Strategy.HIGHEST;
else if ("lowest".equalsIgnoreCase(overrideStrategy))
useStrategy = Strategy.LOWEST;
else if ("exact".equalsIgnoreCase(overrideStrategy))
useStrategy = Strategy.EXACT;
}
}
return useStrategy;
}
/**
* @param bsn
* @param range
* @param attrs
* @param result
* @return
*/
protected Container toContainer(String bsn, String range, Map attrs, File result, DownloadBlocker db) {
File f = result;
if (f == null) {
msgs.ConfusedNoContainerFile();
f = new File("was null");
}
Container container;
if (f.getName().endsWith("lib"))
container = new Container(this, bsn, range, Container.TYPE.LIBRARY, f, null, attrs, db);
else
container = new Container(this, bsn, range, Container.TYPE.REPO, f, null, attrs, db);
return container;
}
/**
* Look for the bundle in the workspace. The premise is that the bsn must
* start with the project name.
*
* @param bsn
* The bsn
* @param attrs
* Any attributes
* @return
* @throws Exception
*/
private Container getBundleFromProject(String bsn, Map attrs) throws Exception {
String pname = bsn;
while (true) {
Project p = getWorkspace().getProject(pname);
if (p != null && p.isValid()) {
Container c = p.getDeliverable(bsn, attrs);
return c;
}
int n = pname.lastIndexOf('.');
if (n <= 0)
return null;
pname = pname.substring(0, n);
}
}
/**
* Deploy the file (which must be a bundle) into the repository.
*
* @param name
* The repository name
* @param file
* bundle
*/
public void deploy(String name, File file) throws Exception {
List plugins = getPlugins(RepositoryPlugin.class);
RepositoryPlugin rp = null;
for (RepositoryPlugin plugin : plugins) {
if (!plugin.canWrite()) {
continue;
}
if (name == null) {
rp = plugin;
break;
} else if (name.equals(plugin.getName())) {
rp = plugin;
break;
}
}
if (rp != null) {
try {
rp.put(new BufferedInputStream(new FileInputStream(file)), new RepositoryPlugin.PutOptions());
return;
}
catch (Exception e) {
msgs.DeployingFile_On_Exception_(file, rp.getName(), e);
}
return;
}
trace("No repo found " + file);
throw new IllegalArgumentException("No repository found for " + file);
}
/**
* Deploy the file (which must be a bundle) into the repository.
*
* @param file
* bundle
*/
public void deploy(File file) throws Exception {
String name = getProperty(Constants.DEPLOYREPO);
deploy(name, file);
}
/**
* Deploy the current project to a repository
*
* @throws Exception
*/
public void deploy() throws Exception {
Parameters deploy = new Parameters(getProperty(DEPLOY));
if (deploy.isEmpty()) {
warning("Deploying but %s is not set to any repo", DEPLOY);
return;
}
File[] outputs = getBuildFiles();
for (File output : outputs) {
for (Deploy d : getPlugins(Deploy.class)) {
trace("Deploying %s to: %s", output.getName(), d);
try {
if (d.deploy(this, output.getName(), new BufferedInputStream(new FileInputStream(output))))
trace("deployed %s successfully to %s", output, d);
}
catch (Exception e) {
msgs.Deploying(e);
}
}
}
}
/**
* Macro access to the repository ${repo;[;[;]]}
*/
static String _repoHelp = "${repo ';' [ ; [; ('HIGHEST'|'LOWEST')]}";
public String _repo(String args[]) throws Exception {
if (args.length < 2) {
msgs.RepoTooFewArguments(_repoHelp, args);
return null;
}
String bsns = args[1];
String version = null;
Strategy strategy = Strategy.HIGHEST;
if (args.length > 2) {
version = args[2];
if (args.length == 4) {
if (args[3].equalsIgnoreCase("HIGHEST"))
strategy = Strategy.HIGHEST;
else if (args[3].equalsIgnoreCase("LOWEST"))
strategy = Strategy.LOWEST;
else if (args[3].equalsIgnoreCase("EXACT"))
strategy = Strategy.EXACT;
else
msgs.InvalidStrategy(_repoHelp, args);
}
}
Collection parts = split(bsns);
List paths = new ArrayList();
for (String bsn : parts) {
Container container = getBundle(bsn, version, strategy, null);
add(paths, container);
}
return join(paths);
}
private void add(List paths, Container container) throws Exception {
if (container.getType() == Container.TYPE.LIBRARY) {
List members = container.getMembers();
for (Container sub : members) {
add(paths, sub);
}
} else {
if (container.getError() == null)
paths.add(container.getFile().getAbsolutePath());
else {
paths.add("<<${repo} = " + container.getBundleSymbolicName() + "-" + container.getVersion() + " : "
+ container.getError() + ">>");
if (isPedantic()) {
warning("Could not expand repo path request: %s ", container);
}
}
}
}
public File getTarget() throws Exception {
prepare();
return target;
}
/**
* This is the external method that will pre-build any dependencies if it is
* out of date.
*
* @param underTest
* @return
* @throws Exception
*/
public File[] build(boolean underTest) throws Exception {
if (isNoBundles())
return null;
if (getProperty("-nope") != null) {
warning("Please replace -nope with %s", NOBUNDLES);
return null;
}
if (isStale()) {
trace("building " + this);
files = buildLocal(underTest);
}
return files;
}
/**
* Return the files
*/
public File[] getFiles() {
return files;
}
/**
* Check if this project needs building. This is defined as:
*/
public boolean isStale() throws Exception {
if (workspace.isOffline()) {
trace("working %s offline, so always stale", this);
return true;
}
Set visited = new HashSet();
return isStale(visited);
}
boolean isStale(Set visited) throws Exception {
// When we do not generate anything ...
if (isNoBundles())
return false;
if (visited.contains(this)) {
msgs.CircularDependencyContext_Message_(this.getName(), visited.toString());
return false;
}
visited.add(this);
long buildTime = 0;
files = getBuildFiles(false);
if (files == null)
return true;
for (File f : files) {
if (f.lastModified() < lastModified())
return true;
if (buildTime < f.lastModified())
buildTime = f.lastModified();
}
for (Project dependency : getDependson()) {
if (dependency == this)
continue;
if (dependency.isStale())
return true;
if (dependency.isNoBundles())
continue;
File[] deps = dependency.getBuildFiles();
for (File f : deps) {
if (f.lastModified() >= buildTime)
return true;
}
}
return false;
}
/**
* This method must only be called when it is sure that the project has been
* build before in the same session. It is a bit yucky, but ant creates
* different class spaces which makes it hard to detect we already build it.
* This method remembers the files in the appropriate instance vars.
*
* @return
*/
public File[] getBuildFiles() throws Exception {
return getBuildFiles(true);
}
public File[] getBuildFiles(boolean buildIfAbsent) throws Exception {
if (files != null)
return files;
File f = new File(getTarget(), BUILDFILES);
if (f.isFile()) {
BufferedReader rdr = IO.reader(f);
try {
List files = newList();
for (String s = rdr.readLine(); s != null; s = rdr.readLine()) {
s = s.trim();
File ff = new File(s);
if (!ff.isFile()) {
// Originally we warned the user
// but lets just rebuild. That way
// the error is not noticed but
// it seems better to correct,
// See #154
rdr.close();
f.delete();
break;
}
files.add(ff);
}
return this.files = files.toArray(new File[files.size()]);
}
finally {
rdr.close();
}
}
if (buildIfAbsent)
return files = buildLocal(false);
return files = null;
}
/**
* Build without doing any dependency checking. Make sure any dependent
* projects are built first.
*
* @param underTest
* @return
* @throws Exception
*/
public File[] buildLocal(boolean underTest) throws Exception {
if (isNoBundles())
return null;
File bfs = new File(getTarget(), BUILDFILES);
bfs.delete();
files = null;
ProjectBuilder builder = getBuilder(null);
try {
if (underTest)
builder.setProperty(Constants.UNDERTEST, "true");
Jar jars[] = builder.builds();
File[] files = new File[jars.length];
getInfo(builder);
if (isOk()) {
this.files = files;
for (int i = 0; i < jars.length; i++) {
Jar jar = jars[i];
files[i] = saveBuild(jar);
}
// Write out the filenames in the buildfiles file
// so we can get them later evenin another process
Writer fw = IO.writer(bfs);
try {
for (File f : files) {
fw.append(f.getAbsolutePath());
fw.append("\n");
}
}
finally {
fw.close();
}
getWorkspace().changedFile(bfs);
return files;
}
return null;
}
finally {
builder.close();
}
}
/**
* Answer if this project does not have any output
*
* @return
*/
public boolean isNoBundles() {
return getProperty(NOBUNDLES) != null;
}
public File saveBuild(Jar jar) throws Exception {
try {
String bsn = jar.getName();
File f = getOutputFile(bsn);
String msg = "";
if (!f.exists() || f.lastModified() < jar.lastModified()) {
reportNewer(f.lastModified(), jar);
f.delete();
File fp = f.getParentFile();
if (!fp.isDirectory()) {
if (!fp.exists() && !fp.mkdirs()) {
throw new IOException("Could not create directory " + fp);
}
}
jar.write(f);
getWorkspace().changedFile(f);
} else {
msg = "(not modified since " + new Date(f.lastModified()) + ")";
}
trace(jar.getName() + " (" + f.getName() + ") " + jar.getResources().size() + " " + msg);
return f;
}
finally {
jar.close();
}
}
public File getOutputFile(String bsn) throws Exception {
return new File(getTarget(), bsn + ".jar");
}
private void reportNewer(long lastModified, Jar jar) {
if (isTrue(getProperty(Constants.REPORTNEWER))) {
StringBuilder sb = new StringBuilder();
String del = "Newer than " + new Date(lastModified);
for (Map.Entry entry : jar.getResources().entrySet()) {
if (entry.getValue().lastModified() > lastModified) {
sb.append(del);
del = ", \n ";
sb.append(entry.getKey());
}
}
if (sb.length() > 0)
warning(sb.toString());
}
}
/**
* Refresh if we are based on stale data. This also implies our workspace.
*/
@Override
public boolean refresh() {
boolean changed = false;
if (isCnf()) {
changed = workspace.refresh();
}
return super.refresh() || changed;
}
public boolean isCnf() {
return getBase().getName().equals(Workspace.CNFDIR);
}
@Override
public void propertiesChanged() {
super.propertiesChanged();
preparedPaths = false;
files = null;
}
public String getName() {
return getBase().getName();
}
public Map getActions() {
Map all = newMap();
Map actions = newMap();
fillActions(all);
getWorkspace().fillActions(all);
for (Map.Entry action : all.entrySet()) {
String key = getReplacer().process(action.getKey());
if (key != null && key.trim().length() != 0)
actions.put(key, action.getValue());
}
return actions;
}
public void fillActions(Map all) {
List plugins = getPlugins(NamedAction.class);
for (NamedAction a : plugins)
all.put(a.getName(), a);
Parameters actions = new Parameters(getProperty("-actions", DEFAULT_ACTIONS));
for (Entry entry : actions.entrySet()) {
String key = Processor.removeDuplicateMarker(entry.getKey());
Action action;
if (entry.getValue().get("script") != null) {
// TODO check for the type
action = new ScriptAction(entry.getValue().get("type"), entry.getValue().get("script"));
} else {
action = new ReflectAction(key);
}
String label = entry.getValue().get("label");
all.put(label.toLowerCase(), action);
}
}
public void release() throws Exception {
release(false);
}
/**
* Release.
*
* @param name
* The repository name
* @throws Exception
*/
public void release(String name) throws Exception {
release(name, false);
}
public void clean() throws Exception {
File target = getTarget0();
if (target.isDirectory() && target.getParentFile() != null) {
IO.delete(target);
if (!target.exists() && !target.mkdirs()) {
throw new IOException("Could not create directory " + target);
}
}
File output = getOutput0();
if (getOutput().isDirectory())
IO.delete(output);
if (!output.exists() && !output.mkdirs()) {
throw new IOException("Could not create directory " + output);
}
}
public File[] build() throws Exception {
return build(false);
}
public void run() throws Exception {
ProjectLauncher pl = getProjectLauncher();
pl.setTrace(isTrace());
pl.launch();
}
public void test() throws Exception {
String testcases = get(Constants.TESTCASES);
if ( testcases == null) {
warning("No %s set", Constants.TESTCASES);
return;
}
clear();
ProjectTester tester = getProjectTester();
tester.setContinuous(isTrue(getProperty(Constants.TESTCONTINUOUS)));
tester.prepare();
if (!isOk()) {
return;
}
int errors = tester.test();
if (errors == 0) {
System.err.println("No Errors");
} else {
if (errors > 0) {
System.err.println(errors + " Error(s)");
} else
System.err.println("Error " + errors);
}
}
/**
* This methods attempts to turn any jar into a valid jar. If this is a
* bundle with manifest, a manifest is added based on defaults. If it is a
* bundle, but not r4, we try to add the r4 headers.
*
* @param descriptor
* @param in
* @return
* @throws Exception
*/
public Jar getValidJar(File f) throws Exception {
Jar jar = new Jar(f);
return getValidJar(jar, f.getAbsolutePath());
}
public Jar getValidJar(URL url) throws Exception {
InputStream in = url.openStream();
try {
Jar jar = new Jar(url.getFile().replace('/', '.'), in, System.currentTimeMillis());
return getValidJar(jar, url.toString());
}
finally {
in.close();
}
}
public Jar getValidJar(Jar jar, String id) throws Exception {
Manifest manifest = jar.getManifest();
if (manifest == null) {
trace("Wrapping with all defaults");
Builder b = new Builder(this);
b.addClasspath(jar);
b.setProperty("Bnd-Message", "Wrapped from " + id + "because lacked manifest");
b.setProperty(Constants.EXPORT_PACKAGE, "*");
b.setProperty(Constants.IMPORT_PACKAGE, "*;resolution:=optional");
jar = b.build();
} else if (manifest.getMainAttributes().getValue(Constants.BUNDLE_MANIFESTVERSION) == null) {
trace("Not a release 4 bundle, wrapping with manifest as source");
Builder b = new Builder(this);
b.addClasspath(jar);
b.setProperty(Constants.PRIVATE_PACKAGE, "*");
b.mergeManifest(manifest);
String imprts = manifest.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
if (imprts == null)
imprts = "";
else
imprts += ",";
imprts += "*;resolution=optional";
b.setProperty(Constants.IMPORT_PACKAGE, imprts);
b.setProperty("Bnd-Message", "Wrapped from " + id + "because had incomplete manifest");
jar = b.build();
}
return jar;
}
public String _project(@SuppressWarnings("unused")
String args[]) {
return getBase().getAbsolutePath();
}
/**
* Bump the version of this project. First check the main bnd file. If this
* does not contain a version, check the include files. If they still do not
* contain a version, then check ALL the sub builders. If not, add a version
* to the main bnd file.
*
* @param mask
* the mask for bumping, see {@link Macro#_version(String[])}
* @throws Exception
*/
public void bump(String mask) throws Exception {
String pattern = "(Bundle-Version\\s*(:|=)\\s*)(([0-9]+(\\.[0-9]+(\\.[0-9]+)?)?))";
String replace = "$1${version;" + mask + ";$3}";
try {
// First try our main bnd file
if (replace(getPropertiesFile(), pattern, replace))
return;
trace("no version in bnd.bnd");
// Try the included filed in reverse order (last has highest
// priority)
List included = getIncluded();
if (included != null) {
List copy = new ArrayList(included);
Collections.reverse(copy);
for (File file : copy) {
if (replace(file, pattern, replace)) {
trace("replaced version in file %s", file);
return;
}
}
}
trace("no version in included files");
boolean found = false;
// Replace in all sub builders.
for (Builder sub : getSubBuilders()) {
found |= replace(sub.getPropertiesFile(), pattern, replace);
}
if (!found) {
trace("no version in sub builders, add it to bnd.bnd");
String bndfile = IO.collect(getPropertiesFile());
bndfile += "\n# Added by by bump\nBundle-Version: 0.0.0\n";
IO.store(bndfile, getPropertiesFile());
}
}
finally {
forceRefresh();
}
}
boolean replace(File f, String pattern, String replacement) throws IOException {
final Macro macro = getReplacer();
Sed sed = new Sed(new Replacer() {
public String process(String line) {
return macro.process(line);
}
}, f);
sed.replace(pattern, replacement);
return sed.doIt() > 0;
}
public void bump() throws Exception {
bump(getProperty(BUMPPOLICY, "=+0"));
}
public void action(String command) throws Throwable {
Map actions = getActions();
Action a = actions.get(command);
if (a == null)
a = new ReflectAction(command);
before(this, command);
try {
a.execute(this, command);
}
catch (Throwable t) {
after(this, command, t);
throw t;
}
}
/**
* Run all before command plugins
*/
void before(@SuppressWarnings("unused")
Project p, String a) {
List testPlugins = getPlugins(CommandPlugin.class);
for (CommandPlugin testPlugin : testPlugins) {
testPlugin.before(this, a);
}
}
/**
* Run all after command plugins
*/
void after(@SuppressWarnings("unused")
Project p, String a, Throwable t) {
List testPlugins = getPlugins(CommandPlugin.class);
for (int i = testPlugins.size() - 1; i >= 0; i--) {
testPlugins.get(i).after(this, a, t);
}
}
public String _findfile(String args[]) {
File f = getFile(args[1]);
List files = new ArrayList();
tree(files, f, "", new Instruction(args[2]));
return join(files);
}
void tree(List list, File current, String path, Instruction instr) {
if (path.length() > 0)
path = path + "/";
String subs[] = current.list();
if (subs != null) {
for (String sub : subs) {
File f = new File(current, sub);
if (f.isFile()) {
if (instr.matches(sub) && !instr.isNegated())
list.add(path + sub);
} else
tree(list, f, path + sub, instr);
}
}
}
public void refreshAll() {
workspace.refresh();
refresh();
}
@SuppressWarnings("unchecked")
public void script(@SuppressWarnings("unused")
String type, String script) throws Exception {
// TODO check tyiping
List scripters = getPlugins(Scripter.class);
if (scripters.isEmpty()) {
msgs.NoScripters_(script);
return;
}
@SuppressWarnings("rawtypes")
Map x = getProperties();
scripters.get(0).eval(x, new StringReader(script));
}
public String _repos(@SuppressWarnings("unused")
String args[]) throws Exception {
List repos = getPlugins(RepositoryPlugin.class);
List names = new ArrayList();
for (RepositoryPlugin rp : repos)
names.add(rp.getName());
return join(names, ", ");
}
public String _help(String args[]) throws Exception {
if (args.length == 1)
return "Specify the option or header you want information for";
Syntax syntax = Syntax.HELP.get(args[1]);
if (syntax == null)
return "No help for " + args[1];
String what = null;
if (args.length > 2)
what = args[2];
if (what == null || what.equals("lead"))
return syntax.getLead();
if (what.equals("example"))
return syntax.getExample();
if (what.equals("pattern"))
return syntax.getPattern();
if (what.equals("values"))
return syntax.getValues();
return "Invalid type specified for help: lead, example, pattern, values";
}
/**
* Returns containers for the deliverables of this project. The deliverables
* is the project builder for this project if no -sub is specified.
* Otherwise it contains all the sub bnd files.
*
* @return A collection of containers
* @throws Exception
*/
public Collection getDeliverables() throws Exception {
List result = new ArrayList();
Collection< ? extends Builder> builders = getSubBuilders();
for (Builder builder : builders) {
Container c = new Container(this, builder.getBsn(), builder.getVersion(), Container.TYPE.PROJECT,
getOutputFile(builder.getBsn()), null, null, null);
result.add(c);
}
return result;
}
/**
* Return the builder associated with the give bnd file or null. The bnd.bnd
* file can contain -sub option. This option allows specifying files in the
* same directory that should drive the generation of multiple deliverables.
* This method figures out if the bndFile is actually one of the bnd files
* of a deliverable.
*
* @param bndFile
* A file pointing to a bnd file.
* @return null or the builder for a sub file.
* @throws Exception
*/
public Builder getSubBuilder(File bndFile) throws Exception {
bndFile = bndFile.getCanonicalFile();
// Verify that we are inside the project.
File base = getBase().getCanonicalFile();
if (!bndFile.getAbsolutePath().startsWith(base.getAbsolutePath()))
return null;
Collection< ? extends Builder> builders = getSubBuilders();
for (Builder sub : builders) {
File propertiesFile = sub.getPropertiesFile();
if (propertiesFile != null) {
if (propertiesFile.getCanonicalFile().equals(bndFile)) {
// Found it!
return sub;
}
}
}
return null;
}
/**
* Answer the container associated with a given bsn.
*
* @param bndFile
* A file pointing to a bnd file.
* @return null or the builder for a sub file.
* @throws Exception
*/
public Container getDeliverable(String bsn, @SuppressWarnings("unused")
Map attrs) throws Exception {
Collection< ? extends Builder> builders = getSubBuilders();
for (Builder sub : builders) {
if (sub.getBsn().equals(bsn))
return new Container(this, getOutputFile(bsn));
}
return null;
}
/**
* Get a list of the sub builders. A bnd.bnd file can contain the -sub
* option. This will generate multiple deliverables. This method returns the
* builders for each sub file. If no -sub option is present, the list will
* contain a builder for the bnd.bnd file.
*
* @return A list of builders.
* @throws Exception
*/
public Collection< ? extends Builder> getSubBuilders() throws Exception {
return getBuilder(null).getSubBuilders();
}
/**
* Calculate the classpath. We include our own runtime.jar which includes
* the test framework and we include the first of the test frameworks
* specified.
*
* @throws Exception
*/
Collection toFile(Collection containers) throws Exception {
ArrayList files = new ArrayList();
for (Container container : containers) {
container.contributeFiles(files, this);
}
return files;
}
public Collection getRunVM() {
Parameters hdr = getParameters(RUNVM);
return hdr.keySet();
}
public Collection getRunProgramArgs() {
Parameters hdr = getParameters(RUNPROGRAMARGS);
return hdr.keySet();
}
public Map getRunProperties() {
return OSGiHeader.parseProperties(getProperty(RUNPROPERTIES));
}
/**
* Get a launcher.
*
* @return
* @throws Exception
*/
public ProjectLauncher getProjectLauncher() throws Exception {
return getHandler(ProjectLauncher.class, getRunpath(), LAUNCHER_PLUGIN, "biz.aQute.launcher");
}
public ProjectTester getProjectTester() throws Exception {
return getHandler(ProjectTester.class, getTestpath(), TESTER_PLUGIN, "biz.aQute.junit");
}
private T getHandler(Class target, Collection containers, String header, String defaultHandler)
throws Exception {
Class< ? extends T> handlerClass = target;
// Make sure we find at least one handler, but hope to find an earlier
// one
List withDefault = Create.list();
withDefault.addAll(containers);
withDefault.addAll(getBundles(Strategy.HIGHEST, defaultHandler, null));
trace("candidates for tester %s", withDefault);
for (Container c : withDefault) {
Manifest manifest = c.getManifest();
if (manifest != null) {
String launcher = manifest.getMainAttributes().getValue(header);
if (launcher != null) {
Class< ? > clz = getClass(launcher, c.getFile());
if (clz != null) {
if (!target.isAssignableFrom(clz)) {
msgs.IncompatibleHandler_For_(launcher, defaultHandler);
} else {
handlerClass = clz.asSubclass(target);
Constructor< ? extends T> constructor = handlerClass.getConstructor(Project.class);
return constructor.newInstance(this);
}
}
}
}
}
throw new IllegalArgumentException("Default handler for " + header + " not found in " + defaultHandler);
}
/**
* Make this project delay the calculation of the run dependencies. The run
* dependencies calculation can be done in prepare or until the dependencies
* are actually needed.
*/
public void setDelayRunDependencies(boolean x) {
delayRunDependencies = x;
}
/**
* Sets the package version on an exported package
*
* @param packageName
* The package name
* @param version
* The new package version
*/
public void setPackageInfo(String packageName, Version version) {
try {
Version current = getPackageInfoJavaVersion(packageName);
boolean packageInfoJava = false;
if (current != null) {
updatePackageInfoJavaFile(packageName, version);
packageInfoJava = true;
}
if (!packageInfoJava || getPackageInfoFile(packageName).exists()) {
updatePackageInfoFile(packageName, version);
}
}
catch (Exception e) {
msgs.SettingPackageInfoException_(e);
}
}
void updatePackageInfoJavaFile(String packageName, final Version newVersion) throws Exception {
File file = getPackageInfoJavaFile(packageName);
if (!file.exists()) {
return;
}
// If package/classes are copied into the bundle through Private-Package
// etc, there will be no source
if (!file.getParentFile().exists()) {
return;
}
Version oldVersion = getPackageInfo(packageName);
if (newVersion.compareTo(oldVersion) == 0) {
return;
}
Sed sed = new Sed(new Replacer() {
public String process(String line) {
Matcher m = VERSION_ANNOTATION.matcher(line);
if (m.find()) {
return line.substring(0, m.start(3)) + newVersion.toString() + line.substring(m.end(3));
}
return line;
}
}, file);
sed.replace(VERSION_ANNOTATION.pattern(), "$0");
sed.setBackup(false);
sed.doIt();
}
void updatePackageInfoFile(String packageName, Version newVersion) throws Exception {
File file = getPackageInfoFile(packageName);
// If package/classes are copied into the bundle through Private-Package
// etc, there will be no source
if (!file.getParentFile().exists()) {
return;
}
Version oldVersion = getPackageInfoVersion(packageName);
if (oldVersion == null) {
oldVersion = Version.emptyVersion;
}
if (newVersion.compareTo(oldVersion) == 0) {
return;
}
PrintWriter pw = IO.writer(file);
pw.println("version " + newVersion);
pw.flush();
pw.close();
String path = packageName.replace('.', '/') + "/packageinfo";
File binary = IO.getFile(getOutput(), path);
File bp = binary.getParentFile();
if (!bp.exists() && !bp.mkdirs()) {
throw new IOException("Could not create directory " + bp);
}
IO.copy(file, binary);
}
File getPackageInfoFile(String packageName) {
String path = packageName.replace('.', '/') + "/packageinfo";
return IO.getFile(getSrc(), path);
}
File getPackageInfoJavaFile(String packageName) {
String path = packageName.replace('.', '/') + "/package-info.java";
return IO.getFile(getSrc(), path);
}
public Version getPackageInfo(String packageName) throws IOException {
Version version = getPackageInfoJavaVersion(packageName);
if (version != null) {
return version;
}
version = getPackageInfoVersion(packageName);
if (version != null) {
return version;
}
return Version.emptyVersion;
}
Version getPackageInfoVersion(String packageName) throws IOException {
File packageInfoFile = getPackageInfoFile(packageName);
if (!packageInfoFile.exists()) {
return null;
}
BufferedReader reader = null;
try {
reader = IO.reader(packageInfoFile);
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("version ")) {
return Version.parseVersion(line.substring(8));
}
}
}
finally {
if (reader != null) {
IO.close(reader);
}
}
return null;
}
Version getPackageInfoJavaVersion(String packageName) throws IOException {
File packageInfoJavaFile = getPackageInfoJavaFile(packageName);
if (!packageInfoJavaFile.exists()) {
return null;
}
BufferedReader reader = null;
try {
reader = IO.reader(packageInfoJavaFile);
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = VERSION_ANNOTATION.matcher(line);
if (matcher.find()) {
return Version.parseVersion(matcher.group(3));
}
}
}
finally {
if (reader != null) {
IO.close(reader);
}
}
return null;
}
/**
* bnd maintains a class path that is set by the environment, i.e. bnd is
* not in charge of it.
*/
public void addClasspath(File f) {
if (!f.isFile() && !f.isDirectory()) {
msgs.AddingNonExistentFileToClassPath_(f);
}
Container container = new Container(f, null);
classpath.add(container);
}
public void clearClasspath() {
classpath.clear();
}
public Collection getClasspath() {
return classpath;
}
}