blob: 5e9d7b01da19597d93325aa013ce3149f7b8598f [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008 Texas Instruments and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--*/
/*
* ======== xdc.services.global.Env ========
*
*! Revision History
*! ================
*! 03-Sep-2008 sasha added '^' to the default package path
*/
package xdc.services.global;
import java.io.*;
import java.util.*;
import xdc.services.spec.Pkg;
/**
* Holder for an XDC session's global properties
*
* There is one Env instance per active XDC session. It holds global
* properties such as the XDC path, the current package root, and
* Java properties. The Java properties can actually be the System
* properties, or a private copy of those.
*/
public class Env
{
private Collection<String> dirvec = null;
private Pkg curPkg = null;
private String curPkgRoot = ".";
private String curPkgName = null;
private Properties properties;
/* ======== Env ======== */
/**
* Default constructor, creates a private copy of the
* current System.Properties.
*/
public Env() {
this(new Properties(System.getProperties()));
}
/**
* Create an instance using the supplied Properties.
*/
public Env(Properties properties) {
this.properties = properties;
}
/* ======== curpath ======== */
/**
* Return the current package path, with all "^" tokens expanded.
*/
public String curpath()
{
if (dirvec == null) {
init();
}
String path = "";
for (String s : dirvec) {
String dir = expand(s);
if (dir.length() != 0) {
path += dir + ";";
}
}
return (path);
}
/* ======== init ======== */
/**
* Initialize this class's data.
*
* dirvec is initialized to a vector of Strings that name package
* repositories. The repository names may contain the character '^';
* this character represents the absolute path to the "current"
* package's repository.
*/
public void init()
{
dirvec = new ArrayList<String>();
/* get any specified complete package path */
String pp = properties.getProperty("xdc.path");
if (pp == null) {
/* if the package path has not been specified at startup (via
* -Dxdc.path=...), we initialize the package path to:
* $XDCPATH;`xdc.root`/packages;^
*/
pp = properties.getProperty("xdc.root");
if (pp == null) {
pp = properties.getProperty("config.rootDir");
}
if (pp == null) {
Err.exit("xdc.root and config.rootDir are both undefined");
}
pp += "/packages;^;";
if (properties.getProperty("XDCPATH") != null) {
pp = properties.getProperty("XDCPATH") + ";" + pp;
}
}
if (!pp.endsWith(";")) {
pp += ';';
}
/* initialize dirvec: one directory per element */
for (int i = pp.indexOf(';'); i != -1; i = pp.indexOf(';')) {
/* BUG? should we get the full path so that changes to
* cwd don't break the path search below?
*/
String tmp = pp.substring(0, i);
if (!tmp.equals("")) {
dirvec.add(tmp);
}
pp = pp.substring(i + 1);
}
/* catch any errors early */
validatePathSyntax();
/* ensure that xdc.path does not contain any ^'s */
properties.setProperty("xdc.path", curpath());
}
/* ======== getProperties ======== */
/**
* Get the Java properties hash.
*/
public Properties getProperties() {
return (properties);
}
/* ======== setProperties ======== */
/**
* Set the Java properties hash.
*/
public void setProperties(Properties properties) {
this.properties = properties;
init();
}
/* ======== getPath ======== */
/**
* Return the current package path, with all "^" tokens expanded.
* @return an array of Strings.
*/
public String[] getPath()
{
if (dirvec == null) {
init();
}
String[] path = dirvec.toArray(new String[dirvec.size()]);
/* expand all "^" tokens */
for (int i = 0; i < path.length; i++) {
path[i] = expand(path[i]);
}
return path;
}
/* ======== setPath ======== */
/**
* Set the current package path.
* @param path an array of Strings.
*/
public void setPath(String[] path)
{
if (dirvec == null) {
init();
}
dirvec = Arrays.asList(path);
/* catch any errors early */
validatePathSyntax();
/* set the global path as a Java property */
properties.setProperty("xdc.path", curpath());
}
public final Pkg getCurPkg() { return this.curPkg; }
public final void setCurPkg( Pkg curPkg ) { this.curPkg = curPkg; }
/* ======== getCurPkgRoot ======== */
/**
* Return the current package root. The package root is the unique
* directory that is the expansion of the "^" character in the XDCPATH.
* @see #expand(String)
*/
public String getCurPkgRoot()
{
if (dirvec == null) {
init();
}
return (curPkgRoot);
}
/* ======== setCurPkgRoot ======== */
/**
* Set the current package root. The package root is the unique
* directory that is the expansion of the "^" character in the XDCPATH.
* @param proot the directory of the package root
* @see #expand(String)
*/
public void setCurPkgRoot(String proot)
{
if (dirvec == null) {
init();
}
if (proot.length() != 0) {
File dir = new java.io.File(proot);
if (!dir.isAbsolute()) {
proot = dir.getAbsolutePath();
}
/* keep Windows separator from confusing other tools */
curPkgRoot = proot.replace('\\', '/');
}
else {
curPkgRoot = "";
}
/* update xdc.path in case dirvec contains '^' tokens */
properties.setProperty("xdc.path", curpath());
}
/* ======== setCurPkgBase ======== */
/**
* Set the current package root, relative to a particular package.
* The package root is the unique directory that is the expansion of
* the "^" character in the XDCPATH.
* The new package root is derived from the package name. For example,
* if the package name is "xdc.bld", then the package root will be set
* to two directory levels above the package directory.
* @param pbase the directory of a package
* @param pname the fully qualified name of a package
* @see #expand(String)
*/
public void setCurPkgBase(String pbase, String pname)
{
if (dirvec == null) {
init();
}
String proot = pbase + "/..";
for (int i = 0; (i = pname.indexOf('.', i) + 1) > 0; ) {
proot += "/..";
}
curPkgName = pname;
setCurPkgRoot(proot);
}
/* ======== getUnnamedPkgName ======== */
/**
* Compute name of "unnamed" package; we need to give a name to all
* packages in the config and build domains. Unnamed packages are
* packages that do not declare a name in their package.xdc
* specification. In this case, the name given to the package is the
* last component of the package's absolute path.
*
* For example, the name of the unnamed package whose package.xdc
* file is located in "/foo/bar/hello/" is just "hello".
*/
public String getUnnamedPkgName()
{
if (dirvec == null) {
init();
}
/* use the current working directory as package directory */
return (getUnnamedPkgName(properties.getProperty("user.dir")));
}
public String getUnnamedPkgName(String fileOrDirName)
{
if (dirvec == null) {
init();
}
/* get cannonical path */
File d = new File(fileOrDirName);
String cp = null;
try {
cp = d.getCanonicalPath();
}
catch (Exception ex) {
Err.exit(ex);
}
/* remove file name in cp (if necessary) */
if (d.isDirectory() != true) {
cp = cp.substring(0, cp.lastIndexOf(File.separatorChar));
}
/* return last component of cp path */
return (cp.substring(cp.lastIndexOf(File.separatorChar) + 1));
}
/* ======== getPathPrefix ======== */
/**
* Get the user supplied package path prefix.
*/
public String getPathPrefix()
{
return (getPathPrefix(false));
}
public String getPathPrefix(boolean cflag)
{
String prefix = properties.getProperty("XDCPATH");
return (prefix == null ? "" : expandRepos(prefix, cflag));
}
/* ======== resolve ======== */
/**
* Find module or interface specification file name from name of
* module or interface.
*
* @param qn qualified module or interface name
* @return file name of the module or interface's specification file.
* Null if the specification file can not be found.
*/
public String resolve(String qname)
{
if (dirvec == null) {
init();
}
String fn = qname.replace('.', File.separatorChar) + ".xdc";
int k = qname.lastIndexOf('.');
if (k == -1) {
return null;
}
/* inspect the current package (if it's defined) */
if (qname.substring(0, k).equals(curPkgName)) {
String res = curPkgRoot + '/' + fn;
File file = new File(res);
if (!file.exists()) {
return (null);
}
try {
return (file.getCanonicalPath().endsWith(fn) ? res : null);
}
catch (Exception e) {
Err.exit(e);
}
}
/* otherwise, search along the package path */
for (String s : dirvec) {
String path = expand(s);
if (path.length() == 0) {
continue;
}
File file = new File(path + '/' + fn);
if (!file.exists()) {
continue;
}
try {
if (file.getCanonicalPath().endsWith(fn)) {
return (path + '/' + fn);
}
} catch (Exception e) {
Err.exit(e);
}
}
/* if we can't find the file, return null */
return (null);
}
/* ======== search ======== */
/**
* Locate fname along the package path (unless it begins with "./")
* and return its canonical file name.
*
* If the name begins with the characters "./", then only the current
* working directory is searched. This provides a mechanism to avoid
* using the package's name when naming a file in the current package.
*
* @return canonical file name if fname exists, otherwise null.
*/
public String search(String fname)
{
if (dirvec == null) {
init();
}
File file = new File(fname);
if (fname.indexOf("./") == 0 || fname.indexOf(".\\") == 0
|| file.isAbsolute()) {
String path = null;
if (file.exists()) {
try {
path = file.getCanonicalPath();
}
catch (IOException x) {
;
}
}
return (path);
}
for (String s : dirvec) {
String path = expand(s);
if (path.length() != 0) {
file = new File(path + '/' + fname);
if (file.exists()) {
try {
path = file.getCanonicalPath();
}
catch (IOException x) {
path = null;
}
return (path);
}
}
}
return (null);
}
/* ======== validate ======== */
/**
* Validate the proposed name for a new package to be created in the
* current directory.
*
* @param pname the dot-separated name of a package.
*
* @return -1 if the package name can't be based in the current working
* directory, or
* 0 if the current working directory is in the package path, or
* n (n > 0) the number directories up from the current working
* directory where pname's repository is.
*/
public int validate(String pname)
{
if (dirvec == null) {
init();
}
if (pname == null) {
return (-1);
}
String fname = "" + File.separatorChar
+ pname.replace('.', File.separatorChar);
/* get the current working directory */
File d = new File(properties.getProperty("user.dir"));
String cwd = null;
try {
cwd = d.getCanonicalPath();
}
catch (Exception ex) {
Err.exit(ex);
}
/* verify that pname specifies directories in the cwd */
if (!cwd.endsWith(fname) && !cwd.endsWith(fname + File.separatorChar)){
/* support package names where one or more elements in the
* name is a symbolic link. This allows one to install a
* package in a directory that has a version number mangled
* into one of the components and create a symbolic link to
* this directory with a name that matches the name declared
* in package.xdc. In this case, multiple versions of the same
* package are installed side-by-side in the same repository
* but only one is "active" (seleected by the symbolic link)
*/
String pdir = pname.replace('.', File.separatorChar);
String cdir = search(pdir);
/* verify that symbolic link alias exists */
if (cdir == null || !cwd.equals(cdir)) {
return (-1);
}
}
/* for each package repository */
for (String s : dirvec) {
String path = expand(s);
if (path.length() == 0) {
continue;
}
File f = new File(path);
if (!f.exists() || !f.isDirectory()) {
continue;
}
/* get full path to this repository (if it exists) */
String cpath = null;
try {
cpath = f.getCanonicalPath();
}
catch (Exception ex) {
Err.exit(ex);
}
/* if cwd is in this repository, return success */
if (cwd.startsWith(cpath)) {
return (0);
}
}
/*
* If we get here the package pname can't be found along the
* current package path but the package is in the current working
* directory. Compute the number of directories up from
* the cwd pname's repository is.
*/
int k = 1;
for (int i = 0; i < pname.length(); i++) {
if (pname.charAt(i) == '.') {
k++;
}
}
return (k);
}
/* ======== expand ======== */
/**
* Expand the package root in the given repository. The current package
* root will be inserted wherever the "^" character occurs in the path.
*/
String expand(String repo)
{
if (repo.indexOf('^') != -1) {
if (curPkgRoot.length() == 0) {
return ("");
}
return (repo.replaceAll("\\^", curPkgRoot));
}
return (repo);
}
/* ======== expandRepos ======== */
/**
* Expand ^ within a list of repositories
*/
private String expandRepos(String pp)
{
return (expandRepos(pp, false));
}
private String expandRepos(String pp, boolean cflag)
{
String result = "";
if (pp != null && pp.length() > 0) {
boolean strip = false;
if (pp.charAt(pp.length() - 1) != ';') {
strip = true;
pp = pp + ";";
}
/* for each non-trivial repo in pp, expand and add to result */
for (int i = pp.indexOf(';'); i != -1; i = pp.indexOf(';')) {
String tmp = expand(pp.substring(0, i));
if (!tmp.equals("")) {
if (cflag) {
try {
tmp = (new File(tmp)).getCanonicalPath();
}
catch (IOException e) {
;
}
}
File tf = new File(tmp);
if (!tf.isAbsolute()) {
tmp = tf.getAbsolutePath();
}
result = result.concat(tmp + ";");
}
pp = pp.substring(i + 1);
}
if (strip && result.length() > 0) {
result = result.substring(0, result.length() - 1);
}
}
return (result);
}
/* ======== validatePathSyntax ======== */
/**
* Look for syntax errors in the directory pathnames
*/
private void validatePathSyntax() {
for (String s : dirvec) {
try {
File f = new File(s);
f.getCanonicalPath();
}
catch (IOException ex) {
throw new RuntimeException(
"An entry on the package path has illegal pathname " +
"syntax: \"" + s + "\" " +
"Full package path is " + curpath()
);
}
}
}
}