mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-24 13:11:47 +00:00
GP-3430 - Updated the gradle buildHelp task to better handle its inputs up-do-date state
This commit is contained in:
parent
0d71657d05
commit
a7668c7f85
@ -127,7 +127,7 @@ if (System.env.LLVM_HOME) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println "Debugger-swig-lldb:buildNatives skipped - LLVM_HOME not defined"
|
||||
logger.debug('Debugger-swig-lldb:buildNatives skipped - LLVM_HOME not defined')
|
||||
}
|
||||
|
||||
task checkLLVM {
|
||||
|
@ -31,10 +31,6 @@ dependencies {
|
||||
api project(':Decompiler')
|
||||
api project(':ProposedUtils')
|
||||
|
||||
helpPath project(path: ':Base', configuration: 'helpPath')
|
||||
helpPath project(path: ':Decompiler', configuration: 'helpPath')
|
||||
helpPath project(path: ':ProgramDiff', configuration: 'helpPath')
|
||||
|
||||
testImplementation project(path: ':Base', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts')
|
||||
|
@ -23,7 +23,6 @@ eclipse.project.name = 'Xtra MachineLearning'
|
||||
|
||||
dependencies {
|
||||
api project(':Base')
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
api "com.oracle.labs.olcut:olcut-config-protobuf:5.2.0" //{exclude group: "com.google.protobuf", module: "protobuf-java"}
|
||||
api ("com.oracle.labs.olcut:olcut-core:5.2.0") {exclude group: "org.jline"}
|
||||
|
@ -61,7 +61,6 @@ dependencies {
|
||||
testImplementation project(path: ':Project', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':DB', configuration: 'testArtifacts')
|
||||
helpPath project(path: ':Docking', configuration: 'helpPath') // this module's help has links to Base help files
|
||||
|
||||
javacc 'net.java.dev.javacc:javacc:5.0'
|
||||
}
|
||||
@ -125,22 +124,29 @@ task buildJavacc {
|
||||
}
|
||||
|
||||
// Note: this must happen before the standard buildHelp for Base
|
||||
task generateExtraHelpFiles {
|
||||
tasks.register('generateExtraHelpFiles') {
|
||||
|
||||
group = 'private'
|
||||
description " Creates any extra help files for Base not covered by the standard build help system"
|
||||
|
||||
def rawTipsFile = file('src/main/resources/ghidra/app/plugin/core/totd/tips.txt')
|
||||
def rawTipsFile = file('src/main/resources/ghidra/app/plugin/core/totd/tips.txt')
|
||||
inputs.file(rawTipsFile)
|
||||
|
||||
def htmlTipsFile = file('src/main/help/help/topics/Misc/Tips.htm')
|
||||
def htmlTipsFile = file('build/help/main/help/topics/Misc/Tips.htm')
|
||||
outputs.file(htmlTipsFile)
|
||||
|
||||
doLast {
|
||||
doLast {
|
||||
createTipsHelpFile(rawTipsFile, htmlTipsFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Base's help includes the file generated by the 'generateExtraHelpFiles' task. Signal that we
|
||||
// depend on that task and it's output file.
|
||||
tasks.named('buildHelp') {
|
||||
dependsOn(tasks.named('generateExtraHelpFiles'))
|
||||
inputs.files tasks.named('generateExtraHelpFiles').get().outputs
|
||||
}
|
||||
|
||||
def createTipsHelpFile(input, output) {
|
||||
// transform original contents - wrap each line in <li> tags
|
||||
def buffy = new StringBuilder()
|
||||
@ -169,7 +175,7 @@ def createTipsHelpFile(input, output) {
|
||||
|
||||
output.text = htmlContent
|
||||
|
||||
println '\n\n\nwrote file ' + output + '\n\n\n'
|
||||
logger.info '\n\n\nwrote file ' + output + '\n\n\n'
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,8 +24,5 @@ eclipse.project.name = 'Features BytePatterns'
|
||||
|
||||
dependencies {
|
||||
api project(':Base')
|
||||
api project(':Utility')
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
api project(':Utility')
|
||||
}
|
||||
|
@ -26,6 +26,4 @@ dependencies {
|
||||
api project(':Base')
|
||||
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
|
||||
helpPath project(path: ':Base', configuration: 'helpPath') // this module's help has links to Base help files
|
||||
}
|
||||
|
@ -32,8 +32,6 @@ dependencies {
|
||||
// include Base src/test/resources when running decompiler integration tests (uses defaultTools)
|
||||
integrationTestImplementation project(path: ':Base', configuration: 'testArtifacts')
|
||||
integrationTestImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
}
|
||||
|
||||
// Include buildable native source in distribution
|
||||
|
@ -47,8 +47,6 @@ dependencies {
|
||||
api 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01'
|
||||
runtimeOnly 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01'
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
// include code from src/test/slow in Base
|
||||
testImplementation project(path: ':Base', configuration: 'integrationTestArtifacts')
|
||||
}
|
||||
|
@ -22,15 +22,9 @@ apply plugin: 'eclipse'
|
||||
|
||||
eclipse.project.name = 'Features Graph FunctionGraph'
|
||||
|
||||
|
||||
|
||||
dependencies {
|
||||
|
||||
api project(":Base")
|
||||
|
||||
api project(":GraphServices")
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
}
|
||||
|
||||
|
||||
|
@ -770,10 +770,6 @@
|
||||
mouse scroll wheel. Disabling this option restores the original function graph scroll wheel
|
||||
behavior of zooming when scrolled.</P>
|
||||
|
||||
<P>The <B>Start Fully Zoomed Out</B> option causes the initial graph to zoom out far enough
|
||||
that the entire graph is displayed. When this option is off a new graph rendering will zoom
|
||||
all the way in (no scaling) to the active vertex.</P>
|
||||
|
||||
<P>The <B>Update Vertex Colors When Grouping</B> option signals to the graph to make the
|
||||
color of the grouped vertex be that of the vertices being grouped.</P>
|
||||
|
||||
|
@ -26,9 +26,7 @@ eclipse.project.name = 'Features FunctionID'
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
api project(":DB")
|
||||
api project(":SoftwareModeling")
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
api project(":SoftwareModeling")
|
||||
}
|
||||
|
||||
// All *.fidb files located in the dependencies/fid directory OR the
|
||||
|
@ -26,14 +26,7 @@ eclipse.project.name = 'Features Graph FunctionCalls'
|
||||
// Note: this module's name is 'GraphFunctionCalls'
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
// This is needed now because we like to the help of the FunctionGraph. If and when that
|
||||
// help is extracted to a higher-level help page, like 'Graphing', then this link should be
|
||||
// removed
|
||||
helpPath project(path: ":FunctionGraph", configuration: 'helpPath')
|
||||
|
||||
|
||||
// These have abstract test classes and stubs needed by this module
|
||||
testImplementation project(path: ':Project', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
|
@ -226,20 +226,88 @@
|
||||
<H2><A name="Satellite_View"></A>Satellite View</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The Satellite View works exactly as the
|
||||
<A href="help/topics/FunctionGraphPlugin/Function_Graph.html#Satellite_View">
|
||||
Function Graph's Satellite View</A>.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
<P>The Satellite View provides an overview of the graph. From this view you may also perform
|
||||
basic adjustment of the overall graph location. In addition to the complete graph, the
|
||||
satellite view contains a <B>lens</B> (the white rectangle) that indicates how much of the
|
||||
current graph fits into the primary view.</P>
|
||||
|
||||
<P>When you single left mouse click in the satellite view the graph is centered around the
|
||||
corresponding point in the primary view. Alternatively, you may drag the lens of the
|
||||
satellite view to the desired location by performing a mouse drag operation on the lens.</P>
|
||||
|
||||
<P>You may hide the satellite view by right-clicking anywhere in the Primary View and
|
||||
deselecting the <B>Display Satellite View</B> toggle button from the popup menu.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="help/shared/tip.png" alt="" border="0"> If the Primary View is painting
|
||||
sluggishly, then hiding the Satellite View cause the Primary View to be more
|
||||
responsive.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Satellite_View_Dock"></A>Detached Satellite</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The Satellite View is attached, or <B>docked</B>, to the Primary View by default.
|
||||
However, you can detach, or undock, the Satellite View, which will put the view into a
|
||||
Component Provider, which itself can be moved, resized and docked anywhere in the Tool you
|
||||
wish.</P>
|
||||
|
||||
<P>To undock the Satellite View, right-click in the graph and deselect the <B>Dock
|
||||
Satellite View</B> menu item.</P>
|
||||
|
||||
<P>To re-dock the Satellite View, right-click in the graph and select the <B>Dock Satellite
|
||||
View</B> menu item.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="help/shared/tip.png" alt="" border="0"> To reshow the Satellite View if it is
|
||||
hidden, whether docked or undocked, you can press the <IMG src=
|
||||
"images/network-wireless.png" alt="" border="1"> button. This button is in the lower-right
|
||||
hand corner of the graph and is only visible if the Satellite View is hidden or
|
||||
undocked.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Options"></A>Options</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The Function Call Graph options are currently a subset of the
|
||||
<A href="help/topics/FunctionGraphPlugin/Function_Graph.html#Options">
|
||||
Function Graph's Options</A>.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P>The <B>Scroll Wheel Pans</B> option signals to move the graph vertical when scrolling the
|
||||
mouse scroll wheel. Disabling this option restores the original function graph scroll wheel
|
||||
behavior of zooming when scrolled.</P>
|
||||
|
||||
<P>The <B>Use Animation</B> option signals to the graph whether to animate mutative graph
|
||||
operations and navigations.</P>
|
||||
|
||||
<P><A name="Layout_Compressing"></A>The <B>Use Condensed Layout</B> option signals to the
|
||||
graph to bring vertices as close together as possible when laying out the graph. Using this
|
||||
option to fit as many vertices on the screen as possible. Disable this option to make the
|
||||
overall layout of the graph more aesthetic.</P>
|
||||
|
||||
<P>The <B>Use Mouse-relative Zoom</B> option signals zoom the graph to and from the mouse
|
||||
location when zooming from the middle-mouse. The default for this option is off, which
|
||||
triggers zoom to work from the center of the graph, regardless of the mouse location.</P>
|
||||
|
||||
<P>The <B>View Settings</B> option describes how the graph will be zoomed when it is first
|
||||
loaded. The values are:</P>
|
||||
|
||||
<UL>
|
||||
<LI><B>Start Fully Zoomed Out</B> - always start fully zoomed out so that the entire
|
||||
graph can be seen.</LI>
|
||||
|
||||
<LI><B>Start Fully Zoomed In</B> - always start fully zoomed in on the vertex containing
|
||||
the current location.</LI>
|
||||
|
||||
<LI><B>Remember User Settings</B> - keep the zoom level where the user previously left
|
||||
it.</LI>
|
||||
</UL>
|
||||
<BR>
|
||||
<BR>
|
||||
|
||||
<P>There are various edge color and highlight color options available to change. The
|
||||
highlight colors are those to be used when the flow animations take place.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
@ -36,8 +36,5 @@ dependencies {
|
||||
api ("org.jgrapht:jgrapht-io:1.5.1") { exclude group: "org.antlr", module: "antlr4-runtime" }
|
||||
|
||||
runtimeOnly "org.jheaps:jheaps:0.13"
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,7 @@ dependencies {
|
||||
|
||||
// Demangler Analyzer needs to find MicrosoftDemangler
|
||||
api project(":MicrosoftDemangler")
|
||||
helpPath project(path: ':Base', configuration: 'helpPath') // this module's help has links to Base help files
|
||||
|
||||
|
||||
testImplementation project(path: ':Base', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
}
|
||||
|
@ -25,6 +25,4 @@ eclipse.project.name = 'Features ProgramDiff'
|
||||
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
}
|
||||
|
@ -25,9 +25,6 @@ eclipse.project.name = 'Features Graph ProgramGraph'
|
||||
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
helpPath project(path: ":GraphServices", configuration: 'helpPath')
|
||||
|
||||
api project(":GraphServices")
|
||||
}
|
||||
|
||||
|
@ -495,8 +495,7 @@
|
||||
<P>These are the display options for graphs that are types of "Program Graphs" such as
|
||||
Call graphs, Block graphs, etc. These types of graphs
|
||||
use program elements as vertices and reference types as edges. See
|
||||
<A href="help/topics/GraphServices/GraphDisplay.htm#Graph_Type_Display_Options">Graph Type Display Options</A> for
|
||||
general help on graph type display options.</P>
|
||||
<B>Graph Type Display Options</B> for general help on graph type display options.</P>
|
||||
</BLOCKQUOTE>
|
||||
<P class="providedbyplugin">Provided by: <I>Program Graph Plugin</I></P>
|
||||
|
||||
|
@ -33,8 +33,7 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
api project(':Base')
|
||||
helpPath project(path: ":Base", configuration: "helpPath")
|
||||
jython JYTHON
|
||||
jython JYTHON
|
||||
api JYTHON
|
||||
}
|
||||
|
||||
|
@ -25,5 +25,4 @@ eclipse.project.name = 'Features SourceCodeLookup'
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
api project(":Decompiler")
|
||||
helpPath project(path: ":Decompiler", configuration: 'helpPath')
|
||||
}
|
||||
|
@ -27,8 +27,6 @@ project.ext.excludeFromParallelIntegrationTests = true
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
|
||||
helpPath project(path: ":Base", configuration: "helpPath")
|
||||
|
||||
testImplementation project(path: ':Project', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
}
|
||||
|
@ -30,6 +30,4 @@ dependencies {
|
||||
// Only include this debug version of the jh library if necessary.
|
||||
//api name:'jh2.with.debug'
|
||||
api 'javax.help:javahelp:2.0.05'
|
||||
|
||||
|
||||
}
|
||||
|
@ -357,17 +357,6 @@ public class GHelpBuilder {
|
||||
errorMessage(buffy.toString());
|
||||
}
|
||||
|
||||
private static void warningMessage(String... message) {
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
buffy.append("\n");
|
||||
buffy.append(" !!!!! WARNING !!!!!\n");
|
||||
for (String string : message) {
|
||||
buffy.append('\t').append('\t').append(string).append('\n');
|
||||
}
|
||||
buffy.append("\n");
|
||||
errorMessage(buffy.toString());
|
||||
}
|
||||
|
||||
private static void printErrorMessage(String message) {
|
||||
// this prevents error messages getting interspersed with output messages
|
||||
flush();
|
||||
|
@ -68,7 +68,11 @@ public class HelpBuildUtils {
|
||||
return new DirectoryHelpModuleLocation(file);
|
||||
}
|
||||
else if (file.isFile()) {
|
||||
return new JarHelpModuleLocation(file);
|
||||
JarHelpModuleLocation jarLocation = JarHelpModuleLocation.fromFile(file);
|
||||
if (jarLocation == null) {
|
||||
HelpBuildUtils.debug("Jar file does not contain help: " + file);
|
||||
}
|
||||
return jarLocation;
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Don't know how to create a help module location for file: " + file);
|
||||
|
@ -15,16 +15,16 @@
|
||||
*/
|
||||
package help;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
import help.validator.LinkDatabase;
|
||||
import help.validator.location.HelpModuleCollection;
|
||||
import help.validator.model.AnchorDefinition;
|
||||
import help.validator.model.GhidraTOCFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class:
|
||||
* <ul>
|
||||
@ -129,7 +129,8 @@ public class JavaHelpFilesBuilder {
|
||||
PrintWriter out = new LogFileWriter(mapFile);
|
||||
try {
|
||||
out.println("<?xml version='1.0' encoding='ISO-8859-1' ?>");
|
||||
out.println("<!doctype MAP public \"-//Sun Microsystems Inc.//DTD JavaHelp Map Version 1.0//EN\">");
|
||||
out.println(
|
||||
"<!doctype MAP public \"-//Sun Microsystems Inc.//DTD JavaHelp Map Version 1.0//EN\">");
|
||||
out.println("<!-- Auto-generated on " + (new Date()).toString() + " : Do Not Edit -->");
|
||||
out.println("<map version=\"1.0\">");
|
||||
|
||||
@ -166,8 +167,8 @@ public class JavaHelpFilesBuilder {
|
||||
}
|
||||
|
||||
if (!parent.endsWith("help")) {
|
||||
throw new AssertException("Map file expected in a directory name 'help'. "
|
||||
+ "Update the map file generation code.");
|
||||
throw new AssertException("Map file expected in a directory name 'help'. " +
|
||||
"Update the map file generation code.");
|
||||
}
|
||||
|
||||
if (!anchorTarget.startsWith("help")) {
|
||||
|
@ -92,8 +92,11 @@ public class UnusedHelpImageFileFinder {
|
||||
}
|
||||
|
||||
SortedSet<Path> set =
|
||||
new TreeSet<>((f1, f2) -> f1.toUri().toString().toLowerCase().compareTo(
|
||||
f2.toUri().toString().toLowerCase()));
|
||||
new TreeSet<>((f1, f2) -> f1.toUri()
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.compareTo(
|
||||
f2.toUri().toString().toLowerCase()));
|
||||
for (Path file : imageFiles) {
|
||||
IMG img = fileToIMGMap.get(file);
|
||||
if (img == null && !isExcludedImageFile(file)) {
|
||||
@ -173,7 +176,10 @@ public class UnusedHelpImageFileFinder {
|
||||
}
|
||||
|
||||
// Create the help directory
|
||||
helpCollections.add(HelpBuildUtils.toLocation(helpDirectoryFile));
|
||||
HelpModuleLocation location = HelpBuildUtils.toLocation(helpDirectoryFile);
|
||||
if (location != null) {
|
||||
helpCollections.add(location);
|
||||
}
|
||||
}
|
||||
|
||||
return helpCollections;
|
||||
|
@ -20,15 +20,16 @@ import java.net.*;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.help.HelpSet;
|
||||
import javax.help.Map.ID;
|
||||
import javax.help.TOCView;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
||||
import help.CustomTOCView.CustomTreeItemDecorator;
|
||||
import help.HelpBuildUtils;
|
||||
import help.TOCItemProvider;
|
||||
import help.CustomTOCView.CustomTreeItemDecorator;
|
||||
import help.validator.model.*;
|
||||
|
||||
/**
|
||||
@ -98,7 +99,10 @@ public class HelpModuleCollection implements TOCItemProvider {
|
||||
}
|
||||
|
||||
private HelpModuleCollection(Collection<HelpModuleLocation> locations) {
|
||||
helpLocations = new LinkedHashSet<>(locations);
|
||||
|
||||
helpLocations = locations.stream()
|
||||
.filter(l -> l != null)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
|
||||
loadTOCs();
|
||||
|
||||
|
@ -22,6 +22,7 @@ import java.util.*;
|
||||
import javax.help.HelpSet;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
import help.HelpBuildUtils;
|
||||
import help.validator.model.*;
|
||||
|
||||
public abstract class HelpModuleLocation {
|
||||
@ -47,13 +48,16 @@ public abstract class HelpModuleLocation {
|
||||
|
||||
public abstract HelpSet loadHelpSet();
|
||||
|
||||
/** Returns true if this help location represents a source of input files to generate help output */
|
||||
/**
|
||||
* Returns true if this help location represents a source of input files to generate help output
|
||||
* @return true if this help location represents a source of input files to generate help output
|
||||
*/
|
||||
public abstract boolean isHelpInputSource();
|
||||
|
||||
protected void loadHelpTopics() {
|
||||
Path helpTopicsDir = helpDir.resolve("topics");
|
||||
if (!Files.exists(helpTopicsDir)) {
|
||||
throw new AssertException("No topics found in help dir: " + helpDir);
|
||||
HelpBuildUtils.debug("No topics found in help dir: " + this);
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> ds = Files.newDirectoryStream(helpTopicsDir);) {
|
||||
@ -65,7 +69,7 @@ public abstract class HelpModuleLocation {
|
||||
}
|
||||
catch (IOException e) {
|
||||
// I suppose there aren't any
|
||||
throw new AssertException("No topics found in help dir: " + helpDir);
|
||||
throw new AssertException("No topics found in help dir: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +197,7 @@ public abstract class HelpModuleLocation {
|
||||
}
|
||||
List<AnchorDefinition> list = map.get(name);
|
||||
if (list == null) {
|
||||
list = new ArrayList<AnchorDefinition>();
|
||||
list = new ArrayList<>();
|
||||
map.put(name, list);
|
||||
}
|
||||
list.add(anchorDefinition);
|
||||
|
@ -21,7 +21,6 @@ import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.help.HelpSet;
|
||||
import javax.help.HelpSetException;
|
||||
@ -32,17 +31,29 @@ import help.validator.model.GhidraTOCFile;
|
||||
|
||||
public class JarHelpModuleLocation extends HelpModuleLocation {
|
||||
|
||||
/*
|
||||
* format of 'helpDir':
|
||||
* jar:file:///.../ghidra-prep/Ghidra/Features/Base/build/libs/Base.jar!/help
|
||||
*/
|
||||
private static final Pattern JAR_FILENAME_PATTERN = Pattern.compile(".*/(\\w*)\\.jar!/.*");
|
||||
|
||||
private static Map<String, String> env = new HashMap<String, String>();
|
||||
private static Map<String, String> env = new HashMap<>();
|
||||
static {
|
||||
env.put("create", "false");
|
||||
}
|
||||
|
||||
public static JarHelpModuleLocation fromFile(File jar) {
|
||||
FileSystem fs = getOrCreateJarFS(jar);
|
||||
Path helpRootPath = fs.getPath("/help");
|
||||
if (!Files.exists(helpRootPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Path topicsPath = helpRootPath.resolve("topics");
|
||||
|
||||
if (!Files.exists(topicsPath)) {
|
||||
// all help locations must have a topics directory; we can get here if the jar contains
|
||||
// a package with the name 'help'
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JarHelpModuleLocation(jar, helpRootPath);
|
||||
}
|
||||
|
||||
private static FileSystem getOrCreateJarFS(File jar) {
|
||||
URI jarURI;
|
||||
try {
|
||||
@ -64,8 +75,8 @@ public class JarHelpModuleLocation extends HelpModuleLocation {
|
||||
}
|
||||
}
|
||||
|
||||
public JarHelpModuleLocation(File file) {
|
||||
super(getOrCreateJarFS(file).getPath("/help"));
|
||||
private JarHelpModuleLocation(File jar, Path path) {
|
||||
super(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -101,9 +112,11 @@ public class JarHelpModuleLocation extends HelpModuleLocation {
|
||||
}
|
||||
|
||||
private String getModuleName(File jarFile) {
|
||||
|
||||
String name = jarFile.getName();
|
||||
int dotIndex = name.indexOf('.');
|
||||
int dotIndex = name.indexOf("-help.jar"); // dev mode
|
||||
if (dotIndex == -1) {
|
||||
dotIndex = name.indexOf(".jar"); // production mode
|
||||
}
|
||||
return name.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,6 @@ package help.validator.location;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import help.validator.location.HelpModuleLocation;
|
||||
|
||||
public abstract class HelpModuleLocationTestDouble extends HelpModuleLocation {
|
||||
|
||||
// this class exists to open up the package-level constructor
|
||||
|
@ -42,14 +42,6 @@ file(ghidraDir + "/application.properties").withReader { reader ->
|
||||
***************************************************************************************/
|
||||
checkGradleVersion()
|
||||
|
||||
configurations {
|
||||
helpPath
|
||||
}
|
||||
|
||||
artifacts {
|
||||
helpPath jar
|
||||
}
|
||||
|
||||
task copyDependencies(type: Copy) {
|
||||
from configurations.runtimeClasspath
|
||||
into "lib"
|
||||
@ -76,7 +68,6 @@ dependencies {
|
||||
api fileTree(dir: ghidraDir + '/Features', include: "**/*.jar")
|
||||
api fileTree(dir: ghidraDir + '/Debug', include: "**/*.jar")
|
||||
api fileTree(dir: ghidraDir + '/Processors', include: "**/*.jar")
|
||||
helpPath fileTree(dir: ghidraDir + '/Features/Base', include: "**/Base.jar")
|
||||
}
|
||||
|
||||
def ZIP_NAME_PREFIX = "${DISTRO_PREFIX}_${RELEASE_NAME}_${getCurrentDate()}"
|
||||
@ -180,73 +171,211 @@ task buildExtension (type: Zip) {
|
||||
}
|
||||
}
|
||||
|
||||
// task for calling the java help indexer
|
||||
task indexHelp(type: JavaExec) {
|
||||
File helpRootDir = file('src/main/help') // this the root dir for the help source
|
||||
File outputFile = file("build/help/main/help/${project.name}_JavaHelpSearch")
|
||||
onlyIf {helpRootDir.exists()}
|
||||
/*********************************************************************************
|
||||
* Help Build Code
|
||||
* Note: This code is derived from helpProject.gradle. Required changes should
|
||||
* be made to that file and then reapplied here.
|
||||
*********************************************************************************/
|
||||
|
||||
dependsOn configurations.helpPath
|
||||
sourceSets {
|
||||
|
||||
// register help resources to be considered inputs to this project; when these resources change,
|
||||
// this project will be considered out-of-date
|
||||
main {
|
||||
resources {
|
||||
srcDir 'src/main/help' // help .html files to be copied to the jar
|
||||
srcDir 'build/help/main' // generated help items (from the indexer); copied to the jar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turns the given file into a 'normalized' path using the Java Path API
|
||||
def normalize(File file) {
|
||||
def path = null;
|
||||
try {
|
||||
path = java.nio.file.Paths.get(file.getAbsolutePath());
|
||||
}
|
||||
catch (Exception e) { // InvalidPathException
|
||||
// we have seen odd strings being placed into the classpath--ignore them
|
||||
return cpPath;
|
||||
}
|
||||
|
||||
def normalizedPath = path.normalize();
|
||||
def absolutePath = normalizedPath.toAbsolutePath();
|
||||
return absolutePath.toString();
|
||||
}
|
||||
|
||||
// Returns the Ghidra module directory for the given file if it is a Ghidra jar file
|
||||
def getModulePathFromJar(File file) {
|
||||
|
||||
String path = normalize(file)
|
||||
String forwardSlashedPath = path.replaceAll("\\\\", "/")
|
||||
def jarPattern = ~'.*/(.*)/(?:lib|build/libs)/(.+).jar'
|
||||
def matcher = jarPattern.matcher(forwardSlashedPath)
|
||||
if (!matcher.matches()) {
|
||||
return null
|
||||
}
|
||||
|
||||
def moduleName = matcher.group(1);
|
||||
def index = forwardSlashedPath.indexOf(moduleName) + moduleName.length()
|
||||
return forwardSlashedPath.substring(0, index)
|
||||
}
|
||||
|
||||
// This method contains logic for calculating help inputs based on the classpath of the project
|
||||
// The work is cached, as the inputs may be requested multiple times during a build
|
||||
ext.helpInputsCache = null
|
||||
def getHelpInputs(Collection fullClasspath) {
|
||||
|
||||
if (ext.helpInputsCache != null) {
|
||||
return ext.helpInputsCache
|
||||
}
|
||||
|
||||
def results = new HashSet<File>()
|
||||
|
||||
fullClasspath.each {
|
||||
|
||||
String moduleDirPath = getModulePathFromJar(it)
|
||||
if (moduleDirPath == null) {
|
||||
return // continue
|
||||
}
|
||||
|
||||
getHelpInputsFromModule(moduleDirPath, results)
|
||||
}
|
||||
|
||||
// the classpath above does not include my module's contents, so add that manually
|
||||
def modulePath = file('.').getAbsolutePath()
|
||||
getHelpInputsFromModule(modulePath, results)
|
||||
|
||||
ext.helpInputsCache = results.findAll(File::exists)
|
||||
return ext.helpInputsCache
|
||||
}
|
||||
|
||||
def getHelpInputsFromModule(String moduleDirPath, Set<File> results) {
|
||||
|
||||
// add all desired directories now and filter later those that do not exist
|
||||
File moduleDir = new File(moduleDirPath)
|
||||
results.add(new File(moduleDir, 'src/main/resources')) // images
|
||||
results.add(new File(moduleDir, 'src/main/help')) // html files
|
||||
|
||||
File dataDir = new File(moduleDir, 'data') // theme properties files
|
||||
if (dataDir.exists()) {
|
||||
FileCollection themeFiles = fileTree(dataDir) {
|
||||
include '**/*.theme.properties'
|
||||
}
|
||||
results.addAll(themeFiles.getFiles())
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the given file is a jar file that contains a '/help/topics' diretory
|
||||
def hasJarHelp(File file) {
|
||||
|
||||
if (!file.exists()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!file.getAbsolutePath().endsWith(".jar")) {
|
||||
return false
|
||||
}
|
||||
|
||||
def fileSystem = null;
|
||||
try {
|
||||
def jarURI = new URI("jar:file://" + file.toURI().getRawPath());
|
||||
fileSystem = java.nio.file.FileSystems.getFileSystem(jarURI);
|
||||
}
|
||||
catch (Exception e) { // FileSystemNotFoundException
|
||||
// handled below
|
||||
}
|
||||
|
||||
if (fileSystem == null) {
|
||||
// not yet created; try to create the file system
|
||||
def jarURI = new URI("jar:file://" + file.toURI().getRawPath());
|
||||
def env = Map.of("create", "false")
|
||||
fileSystem = java.nio.file.FileSystems.newFileSystem(jarURI, env);
|
||||
}
|
||||
|
||||
def topicsPath = fileSystem.getPath("/help/topics");
|
||||
return java.nio.file.Files.exists(topicsPath)
|
||||
}
|
||||
|
||||
tasks.register('cleanHelp') {
|
||||
File helpOutput = file('build/help/main/help')
|
||||
doFirst {
|
||||
delete helpOutput
|
||||
}
|
||||
}
|
||||
|
||||
// Task for calling the java help indexer, which creates a searchable index of the help contents
|
||||
tasks.register('indexHelp', JavaExec) {
|
||||
|
||||
File helpRootDir = file('src/main/help/help')
|
||||
File outputFile = file("build/help/main/help/${project.name}_JavaHelpSearch")
|
||||
|
||||
inputs.dir helpRootDir
|
||||
outputs.dir outputFile
|
||||
|
||||
classpath = sourceSets.main.runtimeClasspath // this modules runtime classpath (contains jhall.jar)
|
||||
|
||||
mainClass = 'com.sun.java.help.search.Indexer' // main class to call
|
||||
|
||||
// tell the indexer where send its output
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
|
||||
args '-db', outputFile.absolutePath
|
||||
mainClass = 'com.sun.java.help.search.Indexer'
|
||||
|
||||
|
||||
// The index has a config file parameter. The only thing we use in the config file
|
||||
// is a root directory path that should be stripped off all the help references to
|
||||
// make them relative instead of absolute
|
||||
|
||||
File configFile = file('build/helpconfig') // this is the config file that we will create
|
||||
|
||||
// create the config file when the task runs and not during configuration.
|
||||
doFirst {
|
||||
|
||||
// gather up all the help files into a file collection
|
||||
FileTree helpFiles = fileTree('src/main/help') {
|
||||
include '**/*.htm'
|
||||
include '**/*.html'
|
||||
}
|
||||
|
||||
// The index has a config file parameter. The only thing we use in the config file
|
||||
// is a root directory path that should be stripped off all the help references to
|
||||
// make them relative instead of absolute
|
||||
File configFile = file('build/helpconfig')
|
||||
|
||||
// create the config file when the task runs and not during configuration.
|
||||
configFile.parentFile.mkdirs();
|
||||
configFile.write "IndexRemove ${helpRootDir.absolutePath}" + File.separator + "\n"
|
||||
}
|
||||
|
||||
// pass the config file we created as an argument to the indexer
|
||||
args '-c',"$configFile"
|
||||
// pass the config file we created as an argument to the indexer
|
||||
args '-c',"$configFile"
|
||||
|
||||
// gather up all the help files into a file collection
|
||||
FileTree helpFiles = fileTree('src/main/help') {
|
||||
include '**/*.htm'
|
||||
include '**/*.html'
|
||||
}
|
||||
|
||||
doFirst {
|
||||
// tell the indexer where send its output
|
||||
args '-db', outputFile.absolutePath
|
||||
|
||||
|
||||
// for each help file that was found, add it as an argument to the indexer
|
||||
helpFiles.each { File file ->
|
||||
args "${file.absolutePath}"
|
||||
}
|
||||
}
|
||||
|
||||
group "private"
|
||||
description "indexes the helps files for this module. [gradle/helpProject.gradle]"
|
||||
}
|
||||
|
||||
// task for building Ghidra help files - depends on the output from the help indexer
|
||||
task buildHelp(type: JavaExec, dependsOn: indexHelp) {
|
||||
|
||||
File helpRootDir = file('src/main/help/help') // help root where topics dir lives
|
||||
File outputDir = file('build/help/main/help') // dir where we want the help files to be generated
|
||||
onlyIf {helpRootDir.exists()}
|
||||
// Task for building Ghidra help files
|
||||
// - depends on the output from the help indexer
|
||||
// - validates help
|
||||
// - the files generated will be placed in a diretory usable during development mode and will
|
||||
// eventually be placed in the <Module>.jar file
|
||||
tasks.register('buildHelp', JavaExec) {
|
||||
|
||||
group "private"
|
||||
|
||||
dependsOn 'indexHelp'
|
||||
|
||||
File helpRootDir = file('src/main/help/help')
|
||||
File outputDir = file('build/help/main/help')
|
||||
|
||||
inputs.dir helpRootDir
|
||||
|
||||
inputs.files({
|
||||
// Note: this must be done lazily in a closure since the classpath is not ready at
|
||||
// configuration time.
|
||||
return getHelpInputs(sourceSets.main.runtimeClasspath.files)
|
||||
})
|
||||
|
||||
outputs.dir outputDir
|
||||
|
||||
classpath = sourceSets.main.runtimeClasspath // this modules runtime classpath (contains jhall.jar)
|
||||
|
||||
mainClass = 'help.GHelpBuilder' // program to run to build help files.
|
||||
mainClass = 'help.GHelpBuilder'
|
||||
|
||||
args '-n', "${project.name}" // use the modules name as the base for the help file name
|
||||
args '-n', "${project.name}" // use the module's name for the help file name
|
||||
|
||||
args '-o', "${outputDir.absolutePath}" // set the output directory arg
|
||||
|
||||
@ -254,21 +383,48 @@ task buildHelp(type: JavaExec, dependsOn: indexHelp) {
|
||||
systemProperties = [
|
||||
"ADDITIONAL_APPLICATION_ROOT_DIRS": "${ghidraInstallDir}/Ghidra"
|
||||
]
|
||||
|
||||
// args '-debug' // print debug info
|
||||
|
||||
doFirst {
|
||||
configurations.helpPath.each {
|
||||
|
||||
//
|
||||
// The classpath needs to include:
|
||||
// 1) the jar of each dependent Module that has already been built
|
||||
// 2) 'src/main/resources'
|
||||
//
|
||||
|
||||
// Each java project and its dependencies are needed to locate each Ghidra module. Each
|
||||
// module is scanned to find the theme properties files in the 'data' directories.
|
||||
classpath += sourceSets.main.runtimeClasspath
|
||||
|
||||
classpath += files('src/main/resources')
|
||||
|
||||
// To build help, the validator needs any other help content that this module may reference.
|
||||
// Add each of these dependencies as an argument to the validator.
|
||||
def helpJars = classpath.findAll(file -> hasJarHelp(file))
|
||||
helpJars.each {
|
||||
args "-hp"
|
||||
args "${it.absolutePath}"
|
||||
}
|
||||
args "${helpRootDir.absolutePath}" // tell the help builder what help dir to process
|
||||
|
||||
// The help dir to process. This needs to be the last argument to the process,
|
||||
// thus, this is why it is inside of this block
|
||||
args "${helpRootDir.absolutePath}"
|
||||
|
||||
// Sigal that any System.out messages from this Java process should be logged at INFO level.
|
||||
// To see this output, run gradle with the '-i' option to show INFO messages.
|
||||
logging.captureStandardOutput LogLevel.INFO
|
||||
}
|
||||
description " Builds the help for this module. [gradle/helpProject.gradle]\n"
|
||||
}
|
||||
|
||||
|
||||
|
||||
// include the help into the module's jar
|
||||
jar {
|
||||
duplicatesStrategy 'exclude'
|
||||
from "build/help/main" // include the generated help index files
|
||||
from "src/main/help" // include the help source files
|
||||
from "src/main/help" // include the help source files
|
||||
archiveVersion = ""
|
||||
}
|
||||
|
||||
@ -276,6 +432,10 @@ jar {
|
||||
jar.dependsOn 'buildHelp'
|
||||
|
||||
|
||||
/*********************************************************************************
|
||||
* End Help Build Code
|
||||
*********************************************************************************/
|
||||
|
||||
|
||||
/*********************************************************************************
|
||||
* Takes the given file and returns a string representing the file path with everything
|
||||
|
@ -18,54 +18,216 @@
|
||||
have content for the Ghidra help system. A gradle project can include help support by adding
|
||||
the following to its build.gradle file.
|
||||
|
||||
apply from: "$rootProject.projectDir/gradle/helpProject.gradle"
|
||||
|
||||
Clients can register dependencies on other help modules by using the
|
||||
'helpPath' configuration, like so:
|
||||
apply from: "$rootProject.projectDir/gradle/helpProject.gradle"
|
||||
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
Note: This code is copied into buildExtension.gradle. All changes to this file should
|
||||
be made to that file.
|
||||
|
||||
|
||||
Help Build System Notes
|
||||
This file contains custom glue coded needed to adapt the structure of Ghidra's help to the
|
||||
Gradle build system. 'Building help' is defined as validating help content, generating
|
||||
all necessary help files used by the Java Help system, and then placing the help and
|
||||
generated content in a place to be consumed by Ghidra, which differs for development mode
|
||||
and production mode. Validating the help content consists of ensuring: hyperlinks point
|
||||
to valid destinations and image references point to existing images. (This is done to find
|
||||
broken help links at build time.)
|
||||
|
||||
This example will put into the 'helpPath' configuration the project path of
|
||||
'Base', using the value of it's 'helpPath' configuration. This brings us to the next
|
||||
point--'helpPath' gets updated when the jar file is built to include the path of
|
||||
the generated jar file. This allows future clients of a given help module to get
|
||||
the jar file path of that help module's output.
|
||||
|
||||
This file supports building help to work in development mode using
|
||||
Eclipse as well as when performing a build of Ghidra. Generated help content is written to:
|
||||
'build/help/main/help/'
|
||||
|
||||
Developent Mode
|
||||
The Eclipse projects are setup so that 'build/help/main' is part of the classpath. This
|
||||
triggers Eclipse to copy help resources to the respective project's 'bin' directory,
|
||||
which makes the help content available at runtime in Eclipse. In this setup no jar
|
||||
files are used at runtime.
|
||||
|
||||
Production Mode
|
||||
In production mode the contents of 'build/help/main' are added to the final output of
|
||||
the Gradle 'jar' task, which will be <Module>.jar.
|
||||
|
||||
Gradle Building
|
||||
During the help build process we place the contents of 'build/help/main' inside of an
|
||||
artifact named <Module>-help.jar. This allows us to depend on these artifacts from the
|
||||
projects we depend upon. Specifically, the 'buildHelpFiles' task depends upon the
|
||||
<Module>-help.jar artifact from all dependent Modules.
|
||||
|
||||
To get Gradle's incremental building to work correctly, the following list of inputs
|
||||
is declared for validating and building the help content:
|
||||
1) Java files in the Help module used to build help - the help building code
|
||||
2) all dependency data/*.theme.properties - for images used by help via theme IDs
|
||||
3) all dependency src/main/help folder - for help content
|
||||
4) all dependency src/main/resources - for images used by help
|
||||
5) This module's equivalent inputs for 2-3
|
||||
|
||||
In order to correctly find these inputs, we use the main runtime classpath to find
|
||||
Ghidra Modules. These modules are then scanned to find these inputs for each module.
|
||||
The final collection of all these items for all dependent Modules is added to the set
|
||||
of task inputs for building help. Thus, when any of the input above change, the help is
|
||||
considered out-of-date and will be rebuilt.
|
||||
|
||||
The help build code is called via a JavaExec call. This call will pass arguments to the
|
||||
process for any dependent Module's help. This dependent help will be inside of the
|
||||
<Module>-help.jar artifact described above. This file contains code to locate those
|
||||
artifacts so they can be passed into the help build java process.
|
||||
|
||||
*****************************************************************************************/
|
||||
|
||||
// The help modules must be configured first so that we can reference its runtime classpath
|
||||
|
||||
configurations {
|
||||
// The Help Build System takes optional paths to resolve dependencies. Build files
|
||||
// that use this 'script plugin' may put project paths into this variable.
|
||||
helpPath
|
||||
}
|
||||
|
||||
artifacts {
|
||||
// The helpPath is updated to include the jar file output of the help build.
|
||||
helpPath jar
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
helpIndex {
|
||||
java {
|
||||
}
|
||||
}
|
||||
main {
|
||||
resources {
|
||||
srcDir 'src/main/help'
|
||||
srcDir 'build/help/main'
|
||||
}
|
||||
}
|
||||
// This represents the Help module jar file. This is required for building help.
|
||||
helpModule
|
||||
|
||||
// This is used by the indexHelp task to configure the jar file dependency
|
||||
helpIndex
|
||||
}
|
||||
|
||||
dependencies {
|
||||
helpIndexImplementation "javax.help:javahelp:2.0.05"
|
||||
helpIndexImplementation project(':Help')
|
||||
|
||||
helpIndex "javax.help:javahelp:2.0.05"
|
||||
|
||||
// signal that we depend on the Help.jar for our Java build files
|
||||
helpModule project(':Help')
|
||||
}
|
||||
|
||||
// Task for calling the java help indexer, which creates a searchable index of the
|
||||
// help contents.
|
||||
task indexHelp(type: JavaExec) {
|
||||
sourceSets {
|
||||
|
||||
// register help resources to be considered inputs to this project; when these resources change,
|
||||
// this project will be considered out-of-date
|
||||
main {
|
||||
resources {
|
||||
srcDir 'src/main/help' // help .html files to be copied to the jar
|
||||
srcDir 'build/help/main' // generated help items (from the indexer); copied to the jar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************************
|
||||
Utility Methods
|
||||
*****************************************************************************************/
|
||||
|
||||
// Turns the given file into a 'normalized' path using the Java Path API
|
||||
def normalize(File file) {
|
||||
def path = null;
|
||||
try {
|
||||
path = java.nio.file.Paths.get(file.getAbsolutePath());
|
||||
}
|
||||
catch (Exception e) { // InvalidPathException
|
||||
// we have seen odd strings being placed into the classpath--ignore them
|
||||
return cpPath;
|
||||
}
|
||||
|
||||
def normalizedPath = path.normalize();
|
||||
def absolutePath = normalizedPath.toAbsolutePath();
|
||||
return absolutePath.toString();
|
||||
}
|
||||
|
||||
// Returns the Ghidra module directory for the given file if it is a Ghidra jar file
|
||||
def getModulePathFromJar(File file) {
|
||||
|
||||
String path = normalize(file)
|
||||
String forwardSlashedPath = path.replaceAll("\\\\", "/")
|
||||
def jarPattern = ~'.*/(.*)/(?:lib|build/libs)/(.+).jar'
|
||||
def matcher = jarPattern.matcher(forwardSlashedPath)
|
||||
if (!matcher.matches()) {
|
||||
return null
|
||||
}
|
||||
|
||||
def moduleName = matcher.group(1);
|
||||
def index = forwardSlashedPath.indexOf(moduleName) + moduleName.length()
|
||||
return forwardSlashedPath.substring(0, index)
|
||||
}
|
||||
|
||||
// Parses the classpath looking for all Module jar file paths, using those to locate the module
|
||||
// that contains that jar file.
|
||||
// Note: In development mode, the <Module>.jar file on the classpath may not actually yet be built.
|
||||
// In that case, we can still use that path to locate the module.
|
||||
def getMyModules(Collection<File> fullClasspath) {
|
||||
return fullClasspath.collect(file -> getModulePathFromJar(file))
|
||||
.findAll(path -> path != null)
|
||||
.collect(path -> new File(path))
|
||||
}
|
||||
|
||||
// This method contains logic for calculating help inputs based on the classpath of the project
|
||||
// The work is cached, as the inputs may be requested multiple times during a build
|
||||
ext.helpInputsCache = null
|
||||
def getHelpInputs(Collection<File> fullClasspath) {
|
||||
|
||||
if (ext.helpInputsCache != null) {
|
||||
return ext.helpInputsCache
|
||||
}
|
||||
|
||||
def results = new HashSet<File>()
|
||||
Collection<File> modules = getMyModules(fullClasspath)
|
||||
modules.each { m ->
|
||||
getHelpInputsFromModule(m.getAbsolutePath(), results)
|
||||
}
|
||||
|
||||
// the classpath above does not include my module's contents, so add that manually
|
||||
def modulePath = file('.').getAbsolutePath()
|
||||
getHelpInputsFromModule(modulePath, results)
|
||||
|
||||
ext.helpInputsCache = results.findAll(File::exists)
|
||||
return ext.helpInputsCache
|
||||
}
|
||||
|
||||
def getHelpInputsFromModule(String moduleDirPath, Set<File> results) {
|
||||
|
||||
// add all desired directories now and filter later those that do not exist
|
||||
File moduleDir = new File(moduleDirPath)
|
||||
results.add(new File(moduleDir, 'src/main/resources')) // images
|
||||
results.add(new File(moduleDir, 'src/main/help')) // html files
|
||||
|
||||
File dataDir = new File(moduleDir, 'data') // theme properties files
|
||||
if (dataDir.exists()) {
|
||||
FileCollection themeFiles = fileTree(dataDir) {
|
||||
include '**/*.theme.properties'
|
||||
}
|
||||
results.addAll(themeFiles.getFiles())
|
||||
}
|
||||
}
|
||||
|
||||
def getModuleResourcesDirs(Collection<File> fullClasspath) {
|
||||
def modules = getMyModules(fullClasspath)
|
||||
return modules.collect(m -> new File(m, 'src/main/resources'))
|
||||
.findAll(dir -> dir.exists())
|
||||
}
|
||||
|
||||
// Locatates 'buildHelp' tasks in projects that this project depends on. The output of the tasks
|
||||
// is the module's help jar, which is only used to build help and not in the final release. The
|
||||
// jar file names follow this format: <Module>-help.jar.
|
||||
def getDependentProjectHelpTasks(Collection<File> fullClasspath) {
|
||||
|
||||
def myModules = getMyModules(fullClasspath)
|
||||
def myProjects = filterProjectsBy(myModules)
|
||||
return myProjects.collect(p -> p.tasks.findByPath('buildHelp'))
|
||||
.findAll(t -> t != null)
|
||||
}
|
||||
|
||||
// Only projects matching the given collection of modules are returned
|
||||
def filterProjectsBy(Collection<File> modules) {
|
||||
return modules.collect(m -> m.getName())
|
||||
.collect(name -> rootProject.findProject(name))
|
||||
.findAll(p -> p != null)
|
||||
}
|
||||
|
||||
/*****************************************************************************************
|
||||
Tasks
|
||||
*****************************************************************************************/
|
||||
|
||||
tasks.register('cleanHelp') {
|
||||
File helpOutput = file('build/help/main/help')
|
||||
doFirst {
|
||||
delete helpOutput
|
||||
}
|
||||
}
|
||||
|
||||
// Task for calling the java help indexer, which creates a searchable index of the help contents
|
||||
tasks.register('indexHelp', JavaExec) {
|
||||
|
||||
group "private"
|
||||
description "indexes the helps files for this module. [gradle/helpProject.gradle]"
|
||||
@ -73,44 +235,42 @@ task indexHelp(type: JavaExec) {
|
||||
File helpRootDir = file('src/main/help/help')
|
||||
File outputFile = file("build/help/main/help/${project.name}_JavaHelpSearch")
|
||||
|
||||
dependsOn configurations.helpPath
|
||||
dependsOn configurations.runtimeClasspath
|
||||
|
||||
inputs.dir helpRootDir
|
||||
outputs.dir outputFile
|
||||
|
||||
classpath = sourceSets.helpIndex.runtimeClasspath
|
||||
classpath = configurations.helpIndex
|
||||
|
||||
mainClass = 'com.sun.java.help.search.Indexer'
|
||||
|
||||
// tell the indexer where send its output
|
||||
args '-db', outputFile.absolutePath
|
||||
|
||||
// The index has a config file parameter. The only thing we use in the config file
|
||||
// is a root directory path that should be stripped off all the help references to
|
||||
// make them relative instead of absolute
|
||||
File configFile = file('build/helpconfig')
|
||||
|
||||
// gather up all the help files into a file collection
|
||||
FileTree helpFiles = fileTree('src/main/help') {
|
||||
include '**/*.htm'
|
||||
include '**/*.html'
|
||||
}
|
||||
|
||||
// pass the config file we created as an argument to the indexer
|
||||
args '-c',"$configFile"
|
||||
|
||||
|
||||
doFirst {
|
||||
|
||||
// gather up all the help files into a file collection
|
||||
FileTree helpFiles = fileTree('src/main/help') {
|
||||
include '**/*.htm'
|
||||
include '**/*.html'
|
||||
}
|
||||
|
||||
if (helpFiles.isEmpty()) {
|
||||
// must have help to index
|
||||
throw new GradleException("No help files found")
|
||||
}
|
||||
|
||||
// The index tool has a config file parameter, which allows you to pass arguments via a file
|
||||
// instead of the command line. This is useful when dealing with file paths. The only
|
||||
// thing we use in the config file is a root directory path that should be stripped off all
|
||||
// the help references to make them relative instead of absolute. We generate this config
|
||||
// file below.
|
||||
File configFile = file('build/helpconfig')
|
||||
|
||||
// create the config file when the task runs and not during configuration.
|
||||
configFile.parentFile.mkdirs();
|
||||
configFile.write "IndexRemove ${helpRootDir.absolutePath}" + File.separator + "\n"
|
||||
|
||||
// pass the config file we created as an argument to the indexer
|
||||
args '-c',"$configFile"
|
||||
|
||||
// tell the indexer where send its output
|
||||
args '-db', outputFile.absolutePath
|
||||
|
||||
// for each help file that was found, add it as an argument to the indexer
|
||||
helpFiles.each { File file ->
|
||||
@ -119,31 +279,49 @@ task indexHelp(type: JavaExec) {
|
||||
}
|
||||
}
|
||||
|
||||
task cleanHelp {
|
||||
File helpOutput = file('build/help/main/help')
|
||||
doFirst {
|
||||
delete helpOutput
|
||||
}
|
||||
}
|
||||
|
||||
// Task for building Ghidra help files
|
||||
// - depends on the output from the help indexer
|
||||
task buildHelp(type: JavaExec, dependsOn: indexHelp) {
|
||||
group rootProject.GHIDRA_GROUP
|
||||
description " Builds the help for this module. [gradle/helpProject.gradle]\n"
|
||||
// - validates help
|
||||
// - the files generated will be placed in a diretory usable during development mode and will
|
||||
// eventually be placed in:
|
||||
// - the <Module>.jar file in production mode, or
|
||||
// - the <Module>-help.jar file in development mode
|
||||
tasks.register('buildHelpFiles', JavaExec) {
|
||||
|
||||
outputs.upToDateWhen {false} // TODO remove this. we should be able to get the inputs right
|
||||
// the issue is if docs get changed in a dependant module,
|
||||
// this modules needs to know to rebuild
|
||||
group "private"
|
||||
|
||||
dependsOn 'indexHelp'
|
||||
|
||||
// Depend on all <Module>-help.jar files for our dependency modules. These jar files must be
|
||||
// built before we run, since the files will be passed to the help builder. Use a closure to
|
||||
// ensure that the classpath is ready when needed.
|
||||
dependsOn({
|
||||
getDependentProjectHelpTasks(sourceSets.main.runtimeClasspath.files)
|
||||
})
|
||||
|
||||
File helpRootDir = file('src/main/help/help')
|
||||
File outputDir = file('build/help/main/help')
|
||||
|
||||
inputs.dir file('src/main/help');
|
||||
File resourcesDir = file('src/main/resources')
|
||||
if (resourcesDir.exists()) {
|
||||
inputs.dir resourcesDir
|
||||
}
|
||||
//
|
||||
// Inputs (used for incremental building):
|
||||
// 1) Java files in the Help module used to build help
|
||||
// 2) all dependency data/**/*.theme.properties - for images used by help via theme IDs
|
||||
// 3) all dependency src/main/help folder - for help content
|
||||
// 4) all dependency src/main/resources - for images used by help
|
||||
// 5) This module's equivalent inputs for 2-3
|
||||
//
|
||||
|
||||
// 1) Java files in the Help module used to build help
|
||||
inputs.files(configurations.helpModule)
|
||||
|
||||
// 2-5) from above
|
||||
inputs.files({
|
||||
// Note: this must be done lazily in a closure since the classpath is not ready at
|
||||
// configuration time.
|
||||
return getHelpInputs(sourceSets.main.runtimeClasspath.files)
|
||||
})
|
||||
|
||||
outputs.dir outputDir
|
||||
|
||||
mainClass = 'help.GHelpBuilder'
|
||||
@ -157,52 +335,93 @@ task buildHelp(type: JavaExec, dependsOn: indexHelp) {
|
||||
|
||||
doFirst {
|
||||
|
||||
// this module's runtime classpath (contains jhall.jar)
|
||||
classpath project(':Help').sourceSets.main.runtimeClasspath
|
||||
|
||||
// include the classpath for the project using this Help gradle script plugin
|
||||
classpath += project.sourceSets.main.runtimeClasspath
|
||||
// since this runs BEFORE resources are copied, include the resources source dir in
|
||||
// the classpath so the help validator can find them
|
||||
classpath += files('src/main/resources')
|
||||
//
|
||||
// The classpath needs to include items used by internal Java code to validate help
|
||||
// resources:
|
||||
// 1) The jar path of each dependent Module. The jar file will be on the 'main' runtime
|
||||
// classpath, but may not yet exist. Regardless, the Java code will use the path to
|
||||
// locate the module for that path.
|
||||
// 2) Each module's 'src/main/resources' dir (this is needed when the jar files from 1
|
||||
// above have not been built)
|
||||
// 3) This module's 'src/main/resources' dir
|
||||
//
|
||||
|
||||
configurations.helpPath.each {
|
||||
args "-hp"
|
||||
args "${it.absolutePath}"
|
||||
}
|
||||
// Each java project and its dependencies are needed to locate each Ghidra module. Each
|
||||
// module is scanned to find the theme properties files in the 'data' directories.
|
||||
classpath += sourceSets.main.runtimeClasspath
|
||||
|
||||
classpath += files(getModuleResourcesDirs(sourceSets.main.runtimeClasspath.files))
|
||||
|
||||
classpath += files('src/main/resources')
|
||||
|
||||
|
||||
// To build help, the validator needs any other help content that this module may reference.
|
||||
// Add each of these dependencies as an argument to the validator.
|
||||
// The dependency file is the <Module>-help.jar file from the 'buildHelp' tasks upon which
|
||||
// we depend.
|
||||
def buildHelpTasks = getDependentProjectHelpTasks(sourceSets.main.runtimeClasspath.files)
|
||||
buildHelpTasks.each {
|
||||
|
||||
def jarFiles = it.outputs.files
|
||||
jarFiles.each { helpJar ->
|
||||
args "-hp"
|
||||
args "${helpJar.absolutePath}"
|
||||
}
|
||||
}
|
||||
|
||||
// The help dir to process. This needs to be the last argument to the process,
|
||||
// thus, this is why it is inside of this block
|
||||
args "${helpRootDir.absolutePath}"
|
||||
|
||||
// Sigal that any System.out messages from this Java process should be logged at INFO level.
|
||||
// To see this output, run gradle with the '-i' option to show INFO messages.
|
||||
logging.captureStandardOutput LogLevel.INFO
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This task creates a jar file that is used by dependent modules only at build time. The name of
|
||||
* this jar is <Module>-help.jar. This is in contrast to each module's jar which itself contains
|
||||
* all help needed in production. The module's jar filename is <Module>.jar.
|
||||
*/
|
||||
tasks.register('buildHelp', Jar) {
|
||||
|
||||
group rootProject.GHIDRA_GROUP
|
||||
description " Builds the help for this module. [gradle/helpProject.gradle]\n"
|
||||
|
||||
dependsOn tasks.named('buildHelpFiles')
|
||||
duplicatesStrategy 'exclude'
|
||||
|
||||
from "build/help/main" // include the generated help and index files from
|
||||
from "src/main/help" // include the help source files
|
||||
|
||||
destinationDirectory = file("build/libs")
|
||||
archiveBaseName = project.name + '-help'
|
||||
}
|
||||
|
||||
|
||||
// Task for finding unused images that are not referenced from Ghidra help files
|
||||
task findUnusedHelp(type: JavaExec) {
|
||||
group rootProject.GHIDRA_GROUP
|
||||
tasks.register('findUnusedHelp', JavaExec) {
|
||||
|
||||
group "private"
|
||||
description " Finds unused help images for this module. [gradle/helpProject.gradle]\n"
|
||||
|
||||
File helpRootDir = file('src/main/help/help')
|
||||
File outputDir = file('build/help/main/help')
|
||||
|
||||
dependsOn configurations.helpPath
|
||||
|
||||
inputs.dir helpRootDir
|
||||
inputs.files(configurations.helpModule)
|
||||
|
||||
mainClass = 'help.validator.UnusedHelpImageFileFinder'
|
||||
|
||||
// args '-debug' // print debug info
|
||||
|
||||
doFirst {
|
||||
classpath project(':Help').sourceSets.main.runtimeClasspath
|
||||
classpath sourceSets.main.runtimeClasspath
|
||||
|
||||
// the current help dir to process
|
||||
args "-hp"
|
||||
args "${helpRootDir.absolutePath}"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -210,7 +429,7 @@ task findUnusedHelp(type: JavaExec) {
|
||||
// include the help into the module's jar
|
||||
jar {
|
||||
duplicatesStrategy 'exclude'
|
||||
from "build/help/main" // include the generated help index files
|
||||
from "build/help/main" // include the generated help and index files
|
||||
from "src/main/help" // include the help source files
|
||||
}
|
||||
|
||||
@ -222,3 +441,4 @@ jar.dependsOn buildHelp
|
||||
// make sure generated help directories exist during prepdev so that the directories are created and
|
||||
// eclipse doesn't complain about missing src directories.
|
||||
rootProject.prepDev.dependsOn buildHelp
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user