Candidate release of source code.

This commit is contained in:
Dan 2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View file

@ -0,0 +1,171 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.application;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties;
import ghidra.framework.GModule;
import utilities.util.FileUtilities;
/**
* The Application Layout base class defines the customizable elements of the application's
* directory structure. Create a subclass to define a custom layout.
* <p>
* If a layout changes in a significant way, the
* {@link ApplicationProperties#APPLICATION_LAYOUT_VERSION_PROPERTY} should be incremented so
* external things like Eclipse GhidraDev know to look in different places for things.
*/
public abstract class ApplicationLayout {
protected ApplicationProperties applicationProperties;
protected Collection<ResourceFile> applicationRootDirs;
protected ResourceFile applicationInstallationDir;
protected Map<String, GModule> modules;
protected File userTempDir;
protected File userCacheDir;
protected File userSettingsDir;
protected ResourceFile extensionArchiveDir;
protected ResourceFile extensionInstallationDir;
/**
* Gets the application properties from the application layout
*
* @return The application properties. Should never be null.
*/
public final ApplicationProperties getApplicationProperties() {
return applicationProperties;
}
/**
* Gets the application root directories from the application layout.
*
* @return A collection of application root directories (or null if not set).
*/
public final Collection<ResourceFile> getApplicationRootDirs() {
return applicationRootDirs;
}
/**
* Gets the application installation directory from the application layout.
*
* @return The application installation directory (or null if not set).
*/
public final ResourceFile getApplicationInstallationDir() {
return applicationInstallationDir;
}
/**
* Gets the application's modules from the application layout.
*
* @return The application's modules as a map (mapping module name to module for convenience).
*/
public final Map<String, GModule> getModules() {
return modules;
}
/**
* Gets the user temp directory from the application layout.
*
* @return The user temp directory (or null if not set).
*/
public final File getUserTempDir() {
return userTempDir;
}
/**
* Gets the user cache directory from the application layout.
*
* @return The user cache directory (or null if not set).
*/
public final File getUserCacheDir() {
return userCacheDir;
}
/**
* Gets the user settings directory from the application layout.
*
* @return The user settings directory (or null if not set).
*/
public final File getUserSettingsDir() {
return userSettingsDir;
}
/**
* Returns the directory where archived Ghidra Extensions are stored.
*
* @return The Ghidra Extensions archive directory. Could be null if the
* {@link ApplicationLayout} does not support Ghidra Extensions.
*
*/
public final ResourceFile getExtensionArchiveDir() {
return extensionArchiveDir;
}
/**
* Returns the Ghidra Extensions installation folder.
*
* @return The Ghidra Extensions installation directory. Could be null if the
* {@link ApplicationLayout} does not support Ghidra Extensions.
*/
public final ResourceFile getExtensionInstallationDir() {
return extensionInstallationDir;
}
/**
* Creates the application's user directories (or ensures they already exist).
*
* @throws IOException if there was a problem creating the application's user directories.
*/
public final void createUserDirs() throws IOException {
if (userTempDir != null) {
if (!FileUtilities.mkdirs(userTempDir)) {
throw new IOException("Failed to create user temp directory: " + userTempDir);
}
FileUtilities.setOwnerOnlyPermissions(userTempDir);
}
if (userCacheDir != null) {
if (!FileUtilities.mkdirs(userCacheDir)) {
throw new IOException("Failed to create user cache directory: " + userCacheDir);
}
FileUtilities.setOwnerOnlyPermissions(userCacheDir);
}
if (userSettingsDir != null) {
if (!FileUtilities.mkdirs(userSettingsDir)) {
throw new IOException(
"Failed to create user settings directory: " + userSettingsDir);
}
FileUtilities.setOwnerOnlyPermissions(userSettingsDir);
}
}
/**
* Checks whether or not the application is using a "single jar" layout. Custom application
* layouts that extend this class can override this method once they determine they are in
* single jar mode.
*
* @return true if the application is using a "single jar" layout; otherwise, false.
*/
public boolean inSingleJarMode() {
return false;
}
}

View file

@ -0,0 +1,51 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.application;
import ghidra.framework.PluggableServiceRegistry;
import java.io.File;
import utilities.util.FileUtilities;
public class ApplicationSettings {
static {
PluggableServiceRegistry.registerPluggableService(ApplicationSettings.class,
new ApplicationSettings());
}
/**
* Returns the directory into which application settings are stored per user, per
* application version.
* @return the directory into which application settings are stored per user, per
* application version.
*/
public static File getUserApplicationSettingsDirectory() {
ApplicationSettings impl =
PluggableServiceRegistry.getPluggableService(ApplicationSettings.class);
return impl.doGetUserApplicationSettingsDirectory();
}
/**
* Aha! This is where any potential subclasses can update the returned value.
*
* @return the directory into which application settings are stored per user, per
* application version.
*/
protected File doGetUserApplicationSettingsDirectory() {
return FileUtilities.createTempDirectory("application.settings_");
}
}

View file

@ -0,0 +1,203 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.application;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties;
import ghidra.framework.OperatingSystem;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
/**
* Utility class for default application things.
*/
public class ApplicationUtilities {
/**
* Searches for default application root directories.
*
* @return A collection of discovered application root directories (could be empty).
*/
public static Collection<ResourceFile> findDefaultApplicationRootDirs() {
Collection<ResourceFile> applicationRootDirs = new ArrayList<>();
ResourceFile applicationRootDir = findPrimaryApplicationRootDir();
if (applicationRootDir != null) {
applicationRootDirs.add(applicationRootDir);
if (SystemUtilities.isInTestingMode() || SystemUtilities.isInDevelopmentMode()) {
applicationRootDirs.addAll(
findApplicationRootDirsFromRepoConfig(applicationRootDir));
}
}
return applicationRootDirs;
}
/**
* Finds the primary application root directory from the classpath. The primary application
* root directory must contain an application.properties file. No other application root
* directories may contain an application.properties file.
*
* @return The primary application root directory, or null if it could not be found.
*/
private static ResourceFile findPrimaryApplicationRootDir() {
String[] classpath = System.getProperty("java.class.path").split(File.pathSeparator);
for (String pathEntry : classpath) {
try {
ResourceFile pathFile = new ResourceFile(new File(pathEntry).getCanonicalPath());
while (pathFile != null && pathFile.exists()) {
if (new ResourceFile(pathFile, ApplicationProperties.PROPERTY_FILE).exists()) {
return pathFile;
}
pathFile = pathFile.getParentFile();
}
}
catch (IOException e) {
Msg.error(ApplicationUtilities.class, "Invalid class path entry: " + pathEntry,
e);
}
}
return null;
}
/**
* Finds all application root directories defined in the repository config file.
*
* @param primaryApplicationRootDir The primary application root directory that may contain the
* repository config file one directory up.
* @return A collection of defined application repository root directories.
*/
private static Collection<ResourceFile> findApplicationRootDirsFromRepoConfig(
ResourceFile primaryApplicationRootDir) {
Collection<ResourceFile> repoApplicationRootDirs = new ArrayList<>();
ResourceFile repoConfigFile =
new ResourceFile(primaryApplicationRootDir.getParentFile(), "ghidra.repos.config");
if (repoConfigFile.isFile()) {
try (BufferedReader reader =
new BufferedReader(new FileReader(repoConfigFile.getFile(false)))) {
String line = null;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#")) {
continue;
}
ResourceFile potentialApplicationRootDir =
new ResourceFile(repoConfigFile.getParentFile().getParentFile(),
line + File.separator + "Ghidra");
if (potentialApplicationRootDir.isDirectory()) {
repoApplicationRootDirs.add(potentialApplicationRootDir);
}
}
}
catch (IOException e) {
Msg.error(ApplicationUtilities.class, "Failed to read: " + repoConfigFile);
}
}
return repoApplicationRootDirs;
}
/**
* Gets the default application's user temp directory.
*
* @param applicationProperties The application properties.
* @return The default application's user temp directory.
* @throws FileNotFoundException if the user temp directory could not be determined.
*/
public static File getDefaultUserTempDir(ApplicationProperties applicationProperties)
throws FileNotFoundException {
String tmpdir = System.getProperty("java.io.tmpdir");
if (tmpdir == null || tmpdir.isEmpty()) {
throw new FileNotFoundException("System property \"java.io.tmpdir\" is not set!");
}
return new File(tmpdir,
SystemUtilities.getUserName() + "-" + applicationProperties.getApplicationName());
}
/**
* Gets the default application's user cache directory.
*
* @param applicationProperties The application properties.
* @return The default application's user cache directory.
* @throws FileNotFoundException if the user cache directory could not be determined.
*/
public static File getDefaultUserCacheDir(ApplicationProperties applicationProperties)
throws FileNotFoundException {
// Look for preset cache directory
String cachedir = System.getProperty("application.cachedir");
if (cachedir != null && !cachedir.isEmpty()) {
return new File(cachedir,
SystemUtilities.getUserName() + "-" + applicationProperties.getApplicationName());
}
// Handle Windows specially
if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) {
File localAppDataDir = null;
String localAppDataDirPath = System.getenv("LOCALAPPDATA"); // e.g., /Users/myname/AppData/Local
if (localAppDataDirPath != null && !localAppDataDirPath.isEmpty()) {
localAppDataDir = new File(localAppDataDirPath);
}
else {
String userHome = System.getProperty("user.home");
if (userHome != null) {
localAppDataDir = new File(userHome, "AppData\\Local");
if (!localAppDataDir.isDirectory()) {
localAppDataDir = new File(userHome, "Local Settings");
}
}
}
if (localAppDataDir != null && localAppDataDir.isDirectory()) {
return new File(localAppDataDir, applicationProperties.getApplicationName());
}
}
// Use user temp directory if platform specific scheme does not exist above or it failed
return getDefaultUserTempDir(applicationProperties);
}
/**
* Gets the default application's user settings directory.
*
* @param applicationProperties The application properties.
* @param installationDirectory The application installation directory.
* @return The application's user settings directory.
* @throws FileNotFoundException if the user settings directory could not be determined.
*/
public static File getDefaultUserSettingsDir(ApplicationProperties applicationProperties,
ResourceFile installationDirectory) throws FileNotFoundException {
String userSettingsDir = System.getProperty("user.home");
if (userSettingsDir == null || userSettingsDir.isEmpty()) {
throw new FileNotFoundException("System property \"user.home\" is not set!");
}
String prefix =
"." + applicationProperties.getApplicationName().replaceAll("\\s", "").toLowerCase();
File applicationParentDir = new File(userSettingsDir, prefix);
String suffix = applicationProperties.getApplicationVersion();
if (SystemUtilities.isInDevelopmentMode()) {
// Add the appication's installation directory name to this variable, so that each
// branch's project user directory is unique.
suffix += "_location_" + installationDirectory.getName();
}
return new File(applicationParentDir, prefix + "-" + suffix);
}
}

View file

@ -0,0 +1,52 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.application;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties;
/**
* The dummy application layout defines the customizable elements of a dummy application's
* directory structure. A dummy application only has a name, an installation/root dir, and
* a user temp directory.
*/
public class DummyApplicationLayout extends ApplicationLayout {
/**
* Constructs a new dummy application layout object.
*
* @throws FileNotFoundException if there was a problem getting a user directory.
*/
public DummyApplicationLayout(String name) throws FileNotFoundException {
// Application properties
applicationProperties = new ApplicationProperties(name);
// Application installation directory
ResourceFile cwd = new ResourceFile(System.getProperty("user.dir"));
applicationInstallationDir = cwd;
// Application root directories
applicationRootDirs = new ArrayList<>();
applicationRootDirs.add(cwd);
// User directories
userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties);
}
}

View file

@ -0,0 +1,154 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.module;
import generic.jar.ResourceFile;
import ghidra.util.Msg;
import java.io.*;
import java.util.*;
import utilities.util.FileUtilities;
public class ModuleManifestFile {
public final static String MODULE_MANIFEST_FILE_NAME = "Module.manifest";
private static final String NAME_IDENTIFIER = "MODULE NAME:";
private static final String DEPENDENCY_IDENTIFIER = "MODULE DEPENDENCY:";
private static final String MODULE_FILE_LICENSE = "MODULE FILE LICENSE:";
private static final String EXCLUDE_FROM_GHIDRA_JAR = "EXCLUDE FROM GHIDRA JAR";
private static final String DATA_SEARCH_IGNORE_DIR = "DATA SEARCH IGNORE DIR:";
private static final String MODULE_DIR_IDENTIFIER = "MODULE DIR:";
// private static final String EXTENSION_SUFFIX = "EXTENSION SUFFIX:";
// private static final String REQUIRES_CLASS_SEARCH = "REQUIRES CLASS SEARCH:";
// private static final String OWNER_IDENTIFIER = "MODULE DIR:";
// private static final String RSRC_IDENTIFIER = "MODULE DIR:";
// private static final String DATA_IDENTIFIER = "MODULE DIR:";
// private static final String PLAF_IDENTIFIER = "MODULE DIR:";
private static final String COMMENT_IDENTIFIER = "#";
private String moduleName;
private boolean excludeFromGhidraJar;
private Map<String, String> fileIPMap = new HashMap<String, String>();
private Set<String> dataSearchIgnoreDirs = new HashSet<String>();
public ModuleManifestFile(File moduleRootDir) throws IOException {
this(new ResourceFile(moduleRootDir));
}
public ModuleManifestFile(ResourceFile moduleRootDir) throws IOException {
ResourceFile file = new ResourceFile(moduleRootDir, MODULE_MANIFEST_FILE_NAME);
if (!file.exists()) {
throw new FileNotFoundException("Missing module manifest file:" +
file.getAbsolutePath());
}
List<String> lines = FileUtilities.getLines(file);
int lineNumber = 1;
for (String line : lines) {
processLine(file, line, lineNumber++);
}
}
public static boolean hasModuleManifest(File moduleRootDir) {
File file = new File(moduleRootDir, MODULE_MANIFEST_FILE_NAME);
return file.exists();
}
public boolean excludeFromGhidraJar() {
return excludeFromGhidraJar;
}
public Map<String, String> getModuleFileIPs() {
return Collections.unmodifiableMap(fileIPMap);
}
private void processLine(ResourceFile file, String configLine, int lineNumber)
throws IOException {
String trimmedLine = configLine.trim();
if (trimmedLine.length() == 0) {
return; // ignore empty lines.
}
else if (trimmedLine.startsWith(NAME_IDENTIFIER)) {
processNameLine(trimmedLine);
}
else if (trimmedLine.startsWith(DEPENDENCY_IDENTIFIER)) {
// ignore for now
}
else if (trimmedLine.startsWith(EXCLUDE_FROM_GHIDRA_JAR)) {
excludeFromGhidraJar = true;
}
else if (trimmedLine.startsWith(MODULE_FILE_LICENSE)) {
processModuleFileLicense(trimmedLine);
}
else if (trimmedLine.startsWith(COMMENT_IDENTIFIER)) {
// this is a comment line--ignore!
}
else if (trimmedLine.startsWith(DATA_SEARCH_IGNORE_DIR)) {
processDataSearchIgnoreDir(trimmedLine);
}
else if (trimmedLine.startsWith(MODULE_DIR_IDENTIFIER)) {
// do nothing for now
}
else {
String message =
"Module manifest file error on line " + (lineNumber + 1) + " of file: " + file +
"\n\t-> Invalid line encountered: " + trimmedLine;
Msg.debug(this, message);
}
}
private void processDataSearchIgnoreDir(String trimmedLine) {
String ignoreDirName = trimmedLine.substring(DATA_SEARCH_IGNORE_DIR.length()).trim();
dataSearchIgnoreDirs.add(ignoreDirName);
}
private void processModuleFileLicense(String line) throws IOException {
String fileAndIPLine = line.substring(MODULE_FILE_LICENSE.length()).trim();
int firstSpace = fileAndIPLine.indexOf(' ');
if (firstSpace < 0) {
fileIPFail(line); // error
}
String filename = fileAndIPLine.substring(0, firstSpace);
String IP = fileAndIPLine.substring(firstSpace + 1);
fileIPMap.put(filename, IP);
}
private void fileIPFail(String line) throws IOException {
throw new IOException("Invalid Module.manifest entry for identifier \"" +
MODULE_FILE_LICENSE + "\".\nThis line requires two parts: 1) " +
"the module-relative file path and filename, and 2) the IP of " +
"that file.\n Found: " + line);
}
private void processNameLine(String line) {
moduleName = line.substring(NAME_IDENTIFIER.length()).trim();
}
public String getModuleName() {
return moduleName;
}
public Set<String> getDataSearchIgnoreDirs() {
return dataSearchIgnoreDirs;
}
}

View file

@ -0,0 +1,334 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.module;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import generic.jar.ResourceFile;
import ghidra.framework.GModule;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
import utilities.util.FileUtilities;
/**
* Utility methods for module related things.
*/
public class ModuleUtilities {
public static final String MANIFEST_FILE_NAME = "Module.manifest";
public static final String MANIFEST_FILE_NAME_UNINSTALLED = "Module.manifest.uninstalled";
public static final String MODULE_LIST = "MODULE_LIST";
/**
* How many directories deep to look for module directories, starting from an application root
* directory. For example, 3 would pick up modules as deep as: root/category/category/module
*/
private static final int MAX_MODULE_DEPTH = 3;
/**
* Checks if the given directory is a module.
*
* @param dir the directory to check.
* @return true if the given directory is a module
*/
public static boolean isModuleDirectory(ResourceFile dir) {
return new ResourceFile(dir, MANIFEST_FILE_NAME).exists();
}
/**
* Returns true if the given path is a module root directory.
*
* @param path the path to check
* @return true if the given path is a module root directory.
*/
public static boolean isModuleDirectory(Path path) {
File file = path.toFile();
return new File(file, MANIFEST_FILE_NAME).exists();
}
/**
* Searches the given root directory for module root directories. Adds any discovered module
* root directories to the given collection.
*
* @param rootDir The directory to start looking for module root directories in.
* @param moduleRootDirs A collection to add discovered module root directories to.
* @return The given collection with any discovered modules added.
*/
public static Collection<ResourceFile> findModuleRootDirectories(ResourceFile rootDir,
Collection<ResourceFile> moduleRootDirs) {
// look for any external GPL modules
findModuleRootDirectoriesHelper(new ResourceFile(rootDir, "../GPL"), moduleRootDirs,
MAX_MODULE_DEPTH);
return findModuleRootDirectoriesHelper(rootDir, moduleRootDirs, MAX_MODULE_DEPTH);
}
private static Collection<ResourceFile> findModuleRootDirectoriesHelper(ResourceFile rootDir,
Collection<ResourceFile> moduleRootDirs, int remainingDepth) {
if (!rootDir.exists() || remainingDepth <= 0) {
return moduleRootDirs;
}
for (ResourceFile subDir : rootDir.listFiles(ResourceFile::isDirectory)) {
if ("build".equals(subDir.getName())) {
continue; // ignore all "build" directories
}
if (ModuleUtilities.isModuleDirectory(subDir)) {
moduleRootDirs.add(subDir);
}
else {
findModuleRootDirectoriesHelper(subDir, moduleRootDirs, remainingDepth - 1);
}
}
return moduleRootDirs;
}
/**
* Searches the given root directories for module root directories. Adds any discovered module
* root directories to the given collection.
*
* @param rootDirs The directories to look for module root directories in.
* @param moduleRootDirs A collection to add discovered module root directories to.
* @return The given collection with any discovered modules added.
*/
public static Collection<ResourceFile> findModuleRootDirectories(
Collection<ResourceFile> rootDirs, Collection<ResourceFile> moduleRootDirs) {
for (ResourceFile rootDir : rootDirs) {
findModuleRootDirectories(rootDir, moduleRootDirs);
}
return moduleRootDirs;
}
/**
* Searches the given jar root directory for module root directories. Uses a "module list"
* file to locate the module root directories. Adds any discovered module root directories
* to the given collection.
*
* @param rootDir The jar directory to start looking for module root directories in.
* @param moduleRootDirs A collection to add discovered module root directories to.
* @return The given collection with any discovered modules added.
* @throws IOException if there was a problem reading the module list file.
*/
public static Collection<ResourceFile> findJarModuleRootDirectories(ResourceFile rootDir,
Collection<ResourceFile> moduleRootDirs) throws IOException {
ResourceFile moduleListFile = new ResourceFile(rootDir, MODULE_LIST);
for (String relativeModulePath : FileUtilities.getLines(moduleListFile)) {
moduleRootDirs.add(new ResourceFile(rootDir, relativeModulePath));
}
return moduleRootDirs;
}
/**
* Searches for modules in a given collection of module root directories.
*
* @param appRootDirs The collection of application root directories associated with the the given
* list of module root directories.
* @param moduleRootDirs A collection of module root directories to search for modules in.
* @return The discovered modules as a map (mapping module name to module for convenience).
*/
public static Map<String, GModule> findModules(Collection<ResourceFile> appRootDirs,
Collection<ResourceFile> moduleRootDirs) {
Map<String, GModule> map = new TreeMap<>();
for (ResourceFile moduleRoot : moduleRootDirs) {
GModule gModule = new GModule(appRootDirs, moduleRoot);
if (map.put(moduleRoot.getName(), gModule) != null) {
StringBuilder collided = new StringBuilder();
for (ResourceFile collideRoot : moduleRootDirs) {
if (moduleRoot.getName().equals(collideRoot.getName())) {
collided.append("\n");
collided.append(collideRoot.getAbsolutePath());
}
}
throw new AssertException(
"Multiple modules collided with same name: " + moduleRoot.getName() + collided);
}
}
return Collections.unmodifiableMap(map);
}
/**
* Gets the "lib" directories from the given modules.
*
* @param modules The modules to get the lib directories of.
* @return A collection of lib directories from the given modules.
*/
public static Collection<ResourceFile> getModuleLibDirectories(Map<String, GModule> modules) {
List<ResourceFile> libraryDirectories = new ArrayList<>();
for (GModule module : modules.values()) {
module.collectExistingModuleDirs(libraryDirectories, "lib");
// In testing mode, we run out of an intermediate build state...the module jars
// live in a build/libs directory. We only want to look in here when testing because
// other run modes (such as a Ghidra release launched from a user's Eclipse) may contain
// build remnants that could cause problems if discovered.
if (SystemUtilities.isInTestingMode()) {
module.collectExistingModuleDirs(libraryDirectories, "libs");
}
}
return libraryDirectories;
}
/**
* Gets the "bin" directories from the given modules.
*
* @param modules The modules to get the bin directories of.
* @return A collection of bin directories from the given modules.
*/
public static Collection<ResourceFile> getModuleBinDirectories(Map<String, GModule> modules) {
List<ResourceFile> binDirectories = new ArrayList<>();
for (GModule module : modules.values()) {
module.collectExistingModuleDirs(binDirectories, "bin/main");
}
return binDirectories;
}
/**
* Returns true if the given path is parented by a module root directory.
* <p>
* For example, given a module path of <tt>/some/dir/features/cool_module/</tt>, then this
* method will return true for these paths:
* <br>
* <br>
* <tt>/some/dir/features/cool_module</tt><br>
* <tt>/some/dir/features/cool_module/some/child/dir</tt>
* <br>
* <br>and false for these paths:
* <br>
* <br>
* <tt>/some/random/path</tt><br>
* <tt>/some/dir/features/</tt>
*
* @param pathName the path name to check
* @return true if the given path is parented by a module root directory.
* @see #isModuleDirectory(Path)
*/
public static boolean isInModule(String pathName) {
return getModule(pathName) != null;
}
/**
* Returns the path of the module containing the given path string, if it is parented by a
* module root directory.
* <p>
* For example, given a module path of <tt>/some/dir/features/cool_module/</tt>, then this
* method will return that module path, given these paths:
* <br>
* <br>
* <tt>/some/dir/features/cool_module</tt><br>
* <tt>/some/dir/features/cool_module/some/child/dir</tt>
* <br>
* <br>and null for these paths:
* <br>
* <br>
* <tt>/some/random/path</tt><br>
* <tt>/some/dir/features/</tt>
*
* @param pathName the path name to check
* @return the module root directory; null if the path is not in a module
* @see #isModuleDirectory(Path)
*/
public static Path getModule(String pathName) {
Path path = toPath(pathName);
while (path != null) {
if (isModuleDirectory(path)) {
return path;
}
path = path.getParent();
}
return null;
}
private static Path toPath(String pathname) {
try {
return Paths.get(pathname);
}
catch (InvalidPathException e) {
Msg.trace(ModuleUtilities.class, "Invalid path: " + pathname);
return null;
}
}
/**
* Returns a file that is the root folder of the repository containing the given file. 'Root'
* here means a folder that contains a repository folder. As an example, given a repo
* structure of:
*
* <p><tt>/userdir/repoRoot/repoDir/.git</tt><br>
*
* <p>then this method, given will produce the following results (input -&gt; output):<br>
*
* <p><tt>/userdir/repoRoot/repoDir/.git -&gt; /userdir/repoRoot</tt>
* <br><tt>/userdir/repoRoot/repoDir -&gt; /userdir/repoRoot</tt>
* <br><tt>/userdir/repoRoot -&gt; /userdir/repoRoot</tt>
*
*
* @param f the child file of the desired repo
* @return a file that is the root folder of the repository containing the given file; null
* if the given file is not under a repo directory or itself a repo root
*/
public static File findRepoRoot(File f) {
if (f == null) {
return null;
}
File repoDir = findRepo(f);
if (repoDir != null) {
return repoDir.getParentFile();
}
// one last check to see if the given file is actually itself a repo root
File[] children = f.listFiles(file -> file.isDirectory());
if (children != null) {
for (File child : children) {
File childRepo = findRepo(child);
if (childRepo != null) {
return childRepo.getParentFile();
}
}
}
return null;
}
/**
* Returns a file that is the repository folder containing the given file. As an example,
* given a repo structure of:
*
* <p><tt>/userdir/repoRoot/repoDir/.git</tt><br>
*
* <p>then this method, given will produce the following results (input -&gt; output):<br>
*
* <p><tt>/userdir/repoRoot/repoDir/.git -&gt; /userdir/repoRoot/repoDir</tt>
* <br><tt>/userdir/repoRoot/repoDir -&gt; /userdir/repoRoot/repoDir</tt>
*
* @param f the child file of the desired repo
* @return a file that is the repo folder of the repository containing the given file; null
* if the given file is not under a repo directory
*/
public static File findRepo(File f) {
if (f == null) {
return null;
}
File testGit = new File(f, ".git");
if (testGit.exists()) {
return f;
}
return findRepo(f.getParentFile());
}
}