From f4a60244b3b6c78512a5b9ca4ee2e46ead744ead Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 13 Feb 2023 18:49:23 -0500 Subject: [PATCH] GP-3081 - Fixed help build for external plugins --- .../application/GenericApplicationLayout.java | 60 ++++++++++++------- .../src/main/java/help/HelpBuildUtils.java | 2 +- .../help/validator/ReferenceTagProcessor.java | 31 +++++----- .../Common/support/buildExtension.gradle | 5 ++ GhidraBuild/Skeleton/build.gradle | 2 +- .../main/help/help/topics/skeleton/help.html | 2 +- 6 files changed, 61 insertions(+), 41 deletions(-) diff --git a/Ghidra/Framework/Generic/src/main/java/generic/application/GenericApplicationLayout.java b/Ghidra/Framework/Generic/src/main/java/generic/application/GenericApplicationLayout.java index 12823cfa16..56caf2d862 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/application/GenericApplicationLayout.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/application/GenericApplicationLayout.java @@ -22,11 +22,12 @@ import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; + import generic.jar.ResourceFile; import ghidra.framework.ApplicationProperties; import ghidra.framework.GModule; import ghidra.util.SystemUtilities; -import util.CollectionUtils; import utility.application.ApplicationLayout; import utility.application.ApplicationUtilities; import utility.module.ClasspathFilter; @@ -39,6 +40,14 @@ import utility.module.ModuleUtilities; */ public class GenericApplicationLayout extends ApplicationLayout { + /** + * System property that allows specification of additional application root dirs. This is used + * for clients that build plugins external to an installation. The property will be parsed + * using {@link File#pathSeparator}, allowing for multiple values. + */ + private static final String ADDITIONAL_APPLICATION_ROOT_DIRS = + "ADDITIONAL_APPLICATION_ROOT_DIRS"; + private static final String NO_RELEASE_NAME = "NO_RELEASE"; /** Dev mode main source bin dir pattern */ @@ -92,19 +101,10 @@ public class GenericApplicationLayout extends ApplicationLayout { } // Modules - if (SystemUtilities.isInDevelopmentMode()) { - - // In development mode we rely on the IDE's classpath to determine which modules to - // include, as opposed to scanning the filesystem. This prevents unrelated modules - // from being used. - - modules = ModuleUtilities.findModules(applicationRootDirs, - ModuleUtilities.findModuleRootDirectories(applicationRootDirs), - new ClasspathFilter()); - } - else { - modules = ModuleUtilities.findModules(applicationRootDirs, applicationRootDirs); - } + Collection moduleRoots = + ModuleUtilities.findModuleRootDirectories(applicationRootDirs); + modules = + ModuleUtilities.findModules(applicationRootDirs, moduleRoots, new ClasspathFilter()); // User directories userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties); @@ -160,17 +160,33 @@ public class GenericApplicationLayout extends ApplicationLayout { } /** - * Get the default list of Application directories. In repo-based - * development mode this includes the root Ghidra directory within each repo. - * When not in development mode, the requirement is that the current working - * directory correspond to the installation root. The first entry will be - * the primary root in both cases. + * Get the default list of Application directories. In repo-based development mode this + * includes the root Ghidra directory within each repo. When not in development mode, the + * requirement is that the current working directory correspond to the installation root. The + * first entry will be the primary root in both cases. * @return root directories */ public static Collection getDefaultApplicationRootDirs() { - if (SystemUtilities.isInDevelopmentMode()) { - return ApplicationUtilities.findDefaultApplicationRootDirs(); + + Set results = new HashSet<>(); + String additionalRootsProperty = System.getProperty(ADDITIONAL_APPLICATION_ROOT_DIRS); + if (!StringUtils.isBlank(additionalRootsProperty)) { + String[] paths = additionalRootsProperty.split(File.pathSeparator); + for (String path : paths) { + ResourceFile file = new ResourceFile(path); + results.add(file); + } } - return CollectionUtils.asList(new ResourceFile(System.getProperty("user.dir"))); + + if (SystemUtilities.isInDevelopmentMode()) { + Collection defaultRoots = + ApplicationUtilities.findDefaultApplicationRootDirs(); + results.addAll(defaultRoots); + } + else { + results.add(new ResourceFile(System.getProperty("user.dir"))); + } + + return results; } } diff --git a/Ghidra/Framework/Help/src/main/java/help/HelpBuildUtils.java b/Ghidra/Framework/Help/src/main/java/help/HelpBuildUtils.java index 5542b298f5..7d6cca5473 100644 --- a/Ghidra/Framework/Help/src/main/java/help/HelpBuildUtils.java +++ b/Ghidra/Framework/Help/src/main/java/help/HelpBuildUtils.java @@ -488,7 +488,7 @@ public class HelpBuildUtils { debug("Exception loading filesystem for uri: " + uri + "\n\t" + e1.getMessage()); } } - return Paths.get(uri); + return null; } /** diff --git a/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java b/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java index 3770b57c6d..1e990a6640 100644 --- a/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java +++ b/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java @@ -17,11 +17,10 @@ package help.validator; import java.io.IOException; import java.net.URISyntaxException; -import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; -import ghidra.util.exception.AssertException; import help.HelpBuildUtils; import help.validator.location.HelpModuleLocation; import help.validator.model.HREF; @@ -30,7 +29,8 @@ import help.validator.model.IMG; public class ReferenceTagProcessor extends TagProcessor { private static final String EOL = System.getProperty("line.separator"); - private static final String STYLESHEET_FILENAME = "DefaultStyle.css"; + private static final String SHARED_STYLESHEET_NAME = "help/shared/DefaultStyle.css"; + private static final Path SHARED_STYLESHEET_PATH = Paths.get(SHARED_STYLESHEET_NAME); private Path htmlFile; private Set styleSheets = new HashSet<>(); @@ -38,7 +38,6 @@ public class ReferenceTagProcessor extends TagProcessor { private boolean readingTitle = false; private final StringBuffer errors = new StringBuffer(); - private final Path defaultStyleSheet; private final AnchorManager anchorManager; private final HelpModuleLocation help; private int errorCount; @@ -46,13 +45,6 @@ public class ReferenceTagProcessor extends TagProcessor { public ReferenceTagProcessor(HelpModuleLocation help, AnchorManager anchorManager) { this.help = help; this.anchorManager = anchorManager; - - Path sharedHelpDir = HelpBuildUtils.getSharedHelpDirectory(); - defaultStyleSheet = sharedHelpDir.resolve(Path.of(STYLESHEET_FILENAME)); - - if (Files.notExists(defaultStyleSheet)) { - throw new AssertException("Cannot find expected stylesheet: " + defaultStyleSheet); - } } @Override @@ -122,9 +114,16 @@ public class ReferenceTagProcessor extends TagProcessor { String href = tagAttributes.get("href"); if (href != null) { - Path css = HelpBuildUtils.getFile(htmlFile, href); - css = css.normalize(); - styleSheets.add(css); // validated later + + // The user has linked to the system stylesheet; no need to resolve + if (SHARED_STYLESHEET_NAME.equals(href)) { + styleSheets.add(SHARED_STYLESHEET_PATH); + } + else { + Path css = HelpBuildUtils.getFile(htmlFile, href); + css = css.normalize(); + styleSheets.add(css); // validated later + } } // } } @@ -175,7 +174,7 @@ public class ReferenceTagProcessor extends TagProcessor { boolean hasDefaultStyleSheet = false; for (Path ss : styleSheets) { - if (defaultStyleSheet.equals(ss)) { + if (SHARED_STYLESHEET_PATH.equals(ss)) { hasDefaultStyleSheet = true; break; } @@ -183,7 +182,7 @@ public class ReferenceTagProcessor extends TagProcessor { if (!hasDefaultStyleSheet) { errorCount++; - errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet + + errors.append("Incorrect stylesheet defined - none match " + SHARED_STYLESHEET_NAME + " in file " + htmlFile + EOL + "\tDiscovered stylesheets: " + styleSheets + EOL); } diff --git a/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle b/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle index bc194f2195..fcf9cb9729 100644 --- a/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle +++ b/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle @@ -253,6 +253,11 @@ task buildHelp(type: JavaExec, dependsOn: indexHelp) { args '-o', "${outputDir.absolutePath}" // set the output directory arg + // register the Ghidra installation as an application root so the help system can find modules + systemProperties = [ + "ADDITIONAL_APPLICATION_ROOT_DIRS": "${ghidraInstallDir}/Ghidra" + ] + doFirst { configurations.helpPath.each { args "-hp" diff --git a/GhidraBuild/Skeleton/build.gradle b/GhidraBuild/Skeleton/build.gradle index 81e2d0e68e..525dd78994 100644 --- a/GhidraBuild/Skeleton/build.gradle +++ b/GhidraBuild/Skeleton/build.gradle @@ -14,9 +14,9 @@ * limitations under the License. */ apply from: "$rootProject.projectDir/gradle/javaProject.gradle" +apply from: "$rootProject.projectDir/gradle/helpProject.gradle" apply plugin: 'eclipse' - eclipse.project.name = '_Skeleton' dependencies { diff --git a/GhidraBuild/Skeleton/src/main/help/help/topics/skeleton/help.html b/GhidraBuild/Skeleton/src/main/help/help/topics/skeleton/help.html index 5a062bc471..1f9d6a1fc7 100644 --- a/GhidraBuild/Skeleton/src/main/help/help/topics/skeleton/help.html +++ b/GhidraBuild/Skeleton/src/main/help/help/topics/skeleton/help.html @@ -10,7 +10,7 @@ Skeleton Help File for a Module - +