| /* --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() | |
| ); | |
| } | |
| } | |
| } | |
| } |