// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.
package wyc.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import wybs.util.StdProject;
import wycc.lang.Command;
import wycc.util.Logger;
import wyfs.lang.Content;
import wyfs.lang.Path;
import wyfs.util.DirectoryRoot;
import wyfs.util.JarFileRoot;
import wyfs.util.VirtualRoot;
/**
* Provides an abstract command from which other commands for controlling the
* Whiley compiler can be derived. Specifically, this class handles all the
* issues related to managing the various project roots, etc.
*
* @author David J. Pearce
*
*/
public abstract class AbstractProjectCommand<T> implements Command<T> {
/**
* The master project content type registry. This is needed for the build
* system to determine the content type of files it finds on the file
* system.
*/
public final Content.Registry registry;
/**
* The whiley path identifies additional items (i.e. libraries or
* directories) which the compiler uses to resolve symbols (e.g. module
* names, functions, etc).
*/
protected ArrayList<Path.Root> whileypath = new ArrayList<>();
/**
* The location in which whiley source files a located, or null if not
* specified. The default value is the current directory.
*/
protected DirectoryRoot whileydir;
/**
* The location in which wyil binary files are stored, or null if not
* specified.
*/
protected DirectoryRoot wyildir;
/**
* The location in which wyal source files are stored, or null if not
* specified.
*/
protected Path.Root wyaldir;
/**
* The location in which wycs binary files are stored, or null if not
* specified.
*/
protected Path.Root wycsdir;
/**
* The logger used for logging system events
*/
protected Logger logger;
/**
* Construct a new instance of this command.
*
* @param registry
* The content registry being used to match files to content
* types.
* @throws IOException
*/
public AbstractProjectCommand(Content.Registry registry, Logger logger) {
this.registry = registry;
this.logger = logger;
}
// =======================================================================
// Configuration Options
// =======================================================================
private static final String[] SCHEMA = {
"whileypath",
"whileydir",
"wyildir",
"wyaldir"
};
@Override
public String[] getOptions() {
return SCHEMA;
}
@Override
public String describe(String option) {
switch(option) {
case "whileypath":
return "Specify where to find compiled Whiley (WyIL) files";
case "whileydir":
return "Specify where to find Whiley source files";
case "wyildir":
return "Specify where to find place compiled Whiley (WyIL) files";
case "wyaldir":
return "Specify where to place generated verification (WyAL) files";
default:
throw new IllegalArgumentException("invalid option \"" + option + "\"");
}
}
@Override
public void set(String option, Object value) throws ConfigurationError {
try {
switch(option) {
case "whileypath":
setWhileypath((String) option);
break;
case "whileydir":
whileydir = new DirectoryRoot((String) value,registry);
break;
case "wyildir":
wyildir = new DirectoryRoot((String) value,registry);
break;
case "wyaldir":
wyaldir = new DirectoryRoot((String) value,registry);
break;
default:
throw new IllegalArgumentException("invalid option \"" + option + "\"");
}
} catch(IOException e) {
throw new ConfigurationError(e);
}
}
@Override
public Object get(String name) {
// TODO Auto-generated method stub
return null;
}
public void setWhileypath(String paths) throws IOException {
whileypath.clear();
// TODO: this should be pushed into AbstractConfigurable
String[] roots = paths.split(":");
for (String root : roots) {
try {
if (root.endsWith(".jar")) {
whileypath.add(new JarFileRoot(root, registry));
} else {
whileypath.add(new DirectoryRoot(root, registry));
}
} catch (IOException e) {
logger.logTimedMessage("Warning: " + root + " is not a valid package root", 0, 0);
}
}
}
public void setWhileydir(File dir) throws IOException {
this.whileydir = new DirectoryRoot(dir,registry);
}
public void setWyildir(File dir) throws IOException {
this.wyildir = new DirectoryRoot(dir,registry);
}
public void setWyaldir(File dir) throws IOException {
this.wyaldir = new DirectoryRoot(dir,registry);
}
// =======================================================================
// Configuration
// =======================================================================
/**
* Construct a new temporary project. This project is temporary because it
* only exists for the life of an execution of this command.
*
* @return
* @throws IOException
*/
protected StdProject initialiseProject() throws IOException {
// Finalise configuration
finaliseConfiguration();
// Add roots and construct project
ArrayList<Path.Root> roots = new ArrayList<>();
roots.add(whileydir);
roots.add(wyildir);
roots.add(wyaldir);
roots.add(wycsdir);
roots.addAll(whileypath);
//
addBootpath(roots);
return new StdProject(roots);
}
/**
* Finalise the given configuration to ensure it is an consistent state.
* This means, in particular, that roots which have not been defined by the
* user are created as necessary.
*/
private void finaliseConfiguration() throws IOException {
whileydir = getDirectoryRoot(whileydir,new DirectoryRoot(".",registry));
wyildir = getDirectoryRoot(wyildir,whileydir);
wyaldir = getAbstractRoot(wyaldir);
wycsdir = getAbstractRoot(wycsdir);
}
/**
* Initialise the bootpath for use with the compiler. The bootpath basically
* identifies the location of the standard library for automatic inclusion
* into the whileypath.
*
* @param roots
* @throws IOException
*/
private void addBootpath(List<Path.Root>roots) throws IOException {
// Configure boot path
String bootpath = System.getProperty("wdk.bootpath");
if (bootpath != null) {
roots.add(new JarFileRoot(bootpath, registry));
}
}
/**
* Construct a root which must correspond to a physical directory.
*
* @throws IOException
*
*/
private DirectoryRoot getDirectoryRoot(DirectoryRoot dir, DirectoryRoot defaulT) throws IOException {
if(dir != null) {
return dir;
} else {
return defaulT;
}
}
/**
* Construct a root which is either virtual or corresponds to a physical
* directory.
*
* @throws IOException
*
*/
private Path.Root getAbstractRoot(Path.Root dir) throws IOException {
if(dir != null) {
return dir;
} else {
return new VirtualRoot(registry);
}
}
}