// Copyright FreeHEP, 2005-2007.
package org.freehep.maven.nar;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.LinkedList;
import java.util.jar.JarFile;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
import org.codehaus.plexus.util.FileUtils;
public class NarManager {
private Log log;
private MavenProject project;
private ArtifactRepository repository;
private AOL defaultAOL;
private String linkerName;
private String[] narTypes = { "noarch", Library.STATIC, Library.SHARED, Library.JNI, Library.PLUGIN };
public NarManager(Log log, ArtifactRepository repository,
MavenProject project, String architecture, String os, Linker linker)
throws MojoFailureException {
this.log = log;
this.repository = repository;
this.project = project;
this.defaultAOL = NarUtil.getAOL(architecture, os, linker, null);
this.linkerName = NarUtil.getLinkerName(architecture, os, linker);
}
/**
* Returns dependencies which are dependent on NAR files (i.e. contain
* NarInfo)
*/
public List/* <NarArtifact> */getNarDependencies(String scope)
throws MojoExecutionException {
List narDependencies = new LinkedList();
for (Iterator i = getDependencies(scope).iterator(); i.hasNext();) {
Artifact dependency = (Artifact) i.next();
log.debug("Examining artifact for NarInfo: "+dependency);
NarInfo narInfo = getNarInfo(dependency);
if (narInfo != null) {
log.debug(" - added as NarDependency");
narDependencies.add(new NarArtifact(dependency, narInfo));
}
}
return narDependencies;
}
/**
* Returns all NAR dependencies by type: noarch, static, dynamic, jni,
* plugin.
*
* @throws MojoFailureException
*/
public Map/* <String, List<AttachedNarArtifact>> */getAttachedNarDependencyMap(
String scope) throws MojoExecutionException, MojoFailureException {
Map attachedNarDependencies = new HashMap();
for (Iterator i = getNarDependencies(scope).iterator(); i.hasNext();) {
Artifact dependency = (Artifact) i.next();
for (int j = 0; j < narTypes.length; j++) {
List artifactList = getAttachedNarDependencies(dependency,
defaultAOL, narTypes[j]);
if (artifactList != null)
attachedNarDependencies.put(narTypes[j], artifactList);
}
}
return attachedNarDependencies;
}
public List/* <AttachedNarArtifact> */getAttachedNarDependencies(
List/* <NarArtifacts> */narArtifacts)
throws MojoExecutionException, MojoFailureException {
return getAttachedNarDependencies(narArtifacts, null);
}
public List/* <AttachedNarArtifact> */getAttachedNarDependencies(
List/* <NarArtifacts> */narArtifacts, String classifier)
throws MojoExecutionException, MojoFailureException {
AOL aol = null;
String type = null;
if (classifier != null) {
int dash = classifier.lastIndexOf('-');
if (dash < 0) {
aol = new AOL(classifier);
type = null;
} else {
aol = new AOL(classifier.substring(0, dash));
type = classifier.substring(dash + 1);
}
}
return getAttachedNarDependencies(narArtifacts, aol, type);
}
/**
* Returns a list of all attached nar dependencies for a specific binding
* and "noarch", but not where "local" is specified
*
* @param scope
* compile, test, runtime, ....
* @param aol
* either a valid aol, noarch or null. In case of null both the
* default getAOL() and noarch dependencies are returned.
* @param type
* noarch, static, shared, jni, or null. In case of null the
* default binding found in narInfo is used.
* @return
* @throws MojoExecutionException
* @throws MojoFailureException
*/
public List/* <AttachedNarArtifact> */getAttachedNarDependencies(
List/* <NarArtifacts> */narArtifacts, AOL aol, String type)
throws MojoExecutionException, MojoFailureException {
boolean noarch = false;
if (aol == null) {
noarch = true;
aol = defaultAOL;
}
List artifactList = new ArrayList();
for (Iterator i = narArtifacts.iterator(); i.hasNext();) {
Artifact dependency = (Artifact) i.next();
NarInfo narInfo = getNarInfo(dependency);
if (noarch) {
artifactList.addAll(getAttachedNarDependencies(dependency,
null, "noarch"));
}
// use preferred binding, unless non existing.
String binding = narInfo.getBinding(aol, type != null ? type
: "static");
// FIXME kludge, but does not work anymore since AOL is now a class
if (aol.equals("noarch")) {
// FIXME no handling of local
artifactList.addAll(getAttachedNarDependencies(dependency,
null, "noarch"));
} else {
artifactList.addAll(getAttachedNarDependencies(dependency, aol,
binding));
}
}
return artifactList;
}
private List/* <AttachedNarArtifact> */getAttachedNarDependencies(
Artifact dependency, AOL aol, String type)
throws MojoExecutionException, MojoFailureException {
log.debug("GetNarDependencies for " + dependency + ", aol: " + aol + ", type: " + type);
List artifactList = new ArrayList();
NarInfo narInfo = getNarInfo(dependency);
String[] nars = narInfo.getAttachedNars(aol, type);
// FIXME Move this to NarInfo....
if (nars != null) {
for (int j = 0; j < nars.length; j++) {
log.debug(" Checking: " + nars[j]);
if (nars[j].equals(""))
continue;
String[] nar = nars[j].split(":", 5);
if (nar.length >= 4) {
try {
String groupId = nar[0].trim();
String artifactId = nar[1].trim();
String ext = nar[2].trim();
String classifier = nar[3].trim();
// translate for instance g++ to gcc...
aol = narInfo.getAOL(aol);
if (aol != null) {
classifier = NarUtil.replace("${aol}", aol.toString(),
classifier);
}
String version = nar.length >= 5 ? nar[4].trim()
: dependency.getVersion();
artifactList.add(new AttachedNarArtifact(groupId,
artifactId, version, dependency.getScope(),
ext, classifier, dependency.isOptional()));
} catch (InvalidVersionSpecificationException e) {
throw new MojoExecutionException(
"Error while reading nar file for dependency "
+ dependency, e);
}
} else {
log.warn("nars property in " + dependency.getArtifactId()
+ " contains invalid field: '" + nars[j]
+ "' for type: " + type);
}
}
}
return artifactList;
}
public NarInfo getNarInfo(Artifact dependency)
throws MojoExecutionException {
// FIXME reported to maven developer list, isSnapshot changes behaviour
// of getBaseVersion, called in pathOf.
if (dependency.isSnapshot())
;
File file = new File(repository.getBasedir(), repository
.pathOf(dependency));
JarFile jar = null;
try {
jar = new JarFile(file);
NarInfo info = new NarInfo(dependency.getGroupId(), dependency
.getArtifactId(), dependency.getVersion(), log);
if (!info.exists(jar))
return null;
info.read(jar);
return info;
} catch (IOException e) {
throw new MojoExecutionException("Error while reading " + file, e);
} finally {
if (jar != null) {
try {
jar.close();
} catch (IOException e) {
// ignore
}
}
}
}
public File getNarFile(Artifact dependency) throws MojoFailureException {
// FIXME reported to maven developer list, isSnapshot changes behaviour
// of getBaseVersion, called in pathOf.
if (dependency.isSnapshot())
;
return new File(repository.getBasedir(), NarUtil.replace("${aol}",
defaultAOL.toString(), repository.pathOf(dependency)));
}
private List getDependencies(String scope) {
if (scope.equals("test")) {
return project.getTestArtifacts();
} else if (scope.equals("runtime")) {
return project.getRuntimeArtifacts();
}
return project.getCompileArtifacts();
}
public void downloadAttachedNars(List/* <NarArtifacts> */narArtifacts,
List remoteRepositories, ArtifactResolver resolver,
String classifier) throws MojoExecutionException,
MojoFailureException {
// FIXME this may not be the right way to do this.... -U ignored and
// also SNAPSHOT not used
List dependencies = getAttachedNarDependencies(narArtifacts, classifier);
for (Iterator i = dependencies.iterator(); i.hasNext();) {
Artifact dependency = (Artifact) i.next();
try {
log.debug("Resolving " + dependency);
resolver.resolve(dependency, remoteRepositories, repository);
} catch (ArtifactNotFoundException e) {
String message = "nar not found " + dependency.getId();
throw new MojoExecutionException(message, e);
} catch (ArtifactResolutionException e) {
String message = "nar cannot resolve " + dependency.getId();
throw new MojoExecutionException(message, e);
}
}
}
public void unpackAttachedNars(List/* <NarArtifacts> */narArtifacts,
ArchiverManager manager, String classifier, String os)
throws MojoExecutionException, MojoFailureException {
log.debug("Unpack called for OS: "+os+", classifier: "+classifier+" for NarArtifacts {");
for (Iterator i = narArtifacts.iterator(); i.hasNext(); ) {
log.debug(" - "+((NarArtifact)i.next()));
}
log.debug("}");
// FIXME, kludge to get to download the -noarch, based on classifier
List dependencies = getAttachedNarDependencies(narArtifacts, classifier);
for (Iterator i = dependencies.iterator(); i.hasNext();) {
Artifact dependency = (Artifact) i.next();
log.debug("Unpack " + dependency);
File file = getNarFile(dependency);
File narLocation = new File(file.getParentFile(), "nar");
File flagFile = new File(narLocation, FileUtils.basename(file
.getPath(), "." + AbstractNarMojo.NAR_EXTENSION)
+ ".flag");
boolean process = false;
if (!narLocation.exists()) {
narLocation.mkdirs();
process = true;
} else if (!flagFile.exists()) {
process = true;
} else if (file.lastModified() > flagFile.lastModified()) {
process = true;
}
if (process) {
try {
unpackNar(manager, file, narLocation);
if (!NarUtil.getOS(os).equals("Windows")) {
NarUtil.makeExecutable(new File(narLocation, "bin/"+defaultAOL),
log);
// FIXME clumsy
if (defaultAOL.hasLinker("g++")) {
NarUtil.makeExecutable(new File(narLocation, "bin/"+NarUtil.replace("g++", "gcc", defaultAOL.toString())),
log);
}
}
if (linkerName.equals("gcc") || linkerName.equals("g++")) {
NarUtil.runRanlib(new File(narLocation, "lib/"+defaultAOL), log);
// FIXME clumsy
if (defaultAOL.hasLinker("g++")) {
NarUtil.runRanlib(new File(narLocation, "lib/"+NarUtil.replace("g++", "gcc", defaultAOL.toString())),
log);
}
}
FileUtils.fileDelete(flagFile.getPath());
FileUtils.fileWrite(flagFile.getPath(), "");
} catch (IOException e) {
log.warn("Cannot create flag file: " + flagFile.getPath());
}
}
}
}
private void unpackNar(ArchiverManager manager, File file, File location)
throws MojoExecutionException {
try {
UnArchiver unArchiver;
unArchiver = manager.getUnArchiver(AbstractNarMojo.NAR_ROLE_HINT);
unArchiver.setSourceFile(file);
unArchiver.setDestDirectory(location);
unArchiver.extract();
} catch (IOException e) {
throw new MojoExecutionException("Error unpacking file: " + file
+ " to: " + location, e);
} catch (NoSuchArchiverException e) {
throw new MojoExecutionException("Error unpacking file: " + file
+ " to: " + location, e);
} catch (ArchiverException e) {
throw new MojoExecutionException("Error unpacking file: " + file
+ " to: " + location, e);
}
}
}