UPDATE: Made a posting on the Gradle forum. Please star this issue so that it gets more attention http://gsfn.us/t/4jedo
I'm in the process of transitioning from a primarily Ant build environment into a Gradle one. One sticking point is injecting Google Analytics and Adsense code into the JavaDoc. This is done by putting java script code into the header or bottom panels. For an example of what I'm currently doing, look at this question CDATA.
The problem with Gradle is that it can't handle newline characters in the string which is to be inserted. If you filter out those characters you break the script. Here is a code sniplet:
task alljavadoc(type: Javadoc) {
source = javadocProjects.collect { project(it).sourceSets.main.allJava }
classpath = files(javadocProjects.collect { project(it).sourceSets.main.compileClasspath })
destinationDir = file("${buildDir}/docs/javadoc")
configure(options) {
header = "this is\na test which should fail"
}
}
The critical part is "header =". If you remove the '\n' character it will work just fine. Otherwise the call to javadoc, which Gradle makes, will fail with the following error:
Successfully started process 'command '/opt/jdk/jdk1.7.0_21/bin/javadoc''
javadoc: error - Illegal package name: ""
javadoc: warning - No source files for package a
javadoc: warning - No source files for package test
javadoc: warning - No source files for package which
javadoc: warning - No source files for package should
javadoc: warning - No source files for package fail
The actual java script that I wish to include is below. Note that I can't hack it by removing new line characters since that will break the script.
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- banner -->
<ins class="adsbygoogle"
style="display:inline-block;width:468px;height:60px"
data-ad-client="ca-pub-xxxxxxxxxxxxxxxxx"
data-ad-slot="xxxxxxxxx"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
As a sanity check I also passed in a string with new line characters directly to javadoc (manual) on the command line and it works just fine.
javadoc foo.java -header "This is a test
and so is this"
The output HTML:
<div class="aboutLanguage"><em>This is a test
and so is this</em></div>
</div>
I have an explanation, but i don't have a solution except for creating a new feature request in Gradle JIRA.
To generate a javadoc Gradle first generates the so-called argfile at build\tmp\javadocTaskName\javadoc.options that contains all individual options and than executes javadoc #full\path\to\build\tmp\javadocTaskName\javadoc.options command.
It is actually quite useful as you can debug the contents of that file by simply invoking javadoc #javadoc.options yourself from the command line.
It is possible to define multi-line values in the argfile by using the \ character at the end of each line inside the multi-line value.
The example header = "this is\na test which should fail" results in
-header 'this is
a test which should fail'
but we need to get
-header 'this is\
a test which should fail'
to tell javadoc that the value continues on the next line.
Now the problem is how to output that \ on each line.
The obvious attempt at header = "this is\\\na test which should fail" does not work, it will result in
-header 'this is\\
a test which should fail'
And even Groovy multi-line or slashy strings will not work and will result in similar double back slashes.
Because Gradle just replaces all single backslashes in the option values. The JavadocOptionFileWriterContext.writeValue(String) method is the culprit, the replaceAll("\\\\", "\\\\\\\\") line in particular (a regex that matches single backslash and replaces it with double backslash ).
This escaping is required for backslashes inside a line, but it should not escape a single backslash followed by the new line character. My regex-fu is not strong enough to write such a pattern, but it is surely possible.
Or even better, the escaping mechanism inside that method should replace newline characters with a single backslash followed by the newline to hide all this stuff and allow users to declare multi-line javadoc options without the need to think or even know that feature.
I would appreciate if somebody can create an issue in Gradle tracker as i can't do so from my current location. This sentence should be replaced with the link to the issue so that people with similar problem can vote and track its progress.
I tried to implement it in Gradle but I couldn't get it to work reliably on windows. If the options file has this:
-header 'this is\
a test which should fail'
It works nicely on linux/mac but fails on windows (tried on win7/java7 and some other windows+java6). I've tried with vanilla javadoc executable (without Gradle).
I'll get the fix into Gradle and it will work out of the box for linux/mac but not quite for windows. If you want to help out with windows support catch us at http://issues.gradle.org/browse/GRADLE-3099
Related
Below is basically an MCVE of my full problem, which is much messier. What you need to know is that the following line runs when directly put in terminal:
java -classpath /path/to/weka.jar weka.filters.MultiFilter \
-F "weka.filters.unsupervised.attribute.ClusterMembership -I first" \
-i /path/to/in.arff
This is relatively straightforward. Basically, all I am doing is trying to cluster the data from in.arff using all of the default settings for the ClusterMembership filter, but I want to ignore the first attribute. I have the MultiFilter there because in my actual project, there are other filters, so I need this to stay. Like previously mentioned, this works fine. However, when I try to run the same line with ProcessBuilder, I get a "quote parse error", and it seems like the whole structure of nesting quotes breaks down. One way of demonstrating this is trying to get the following to work:
List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp");
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("\"weka.filters.unsupervised.attribute.ClusterMembership");
args.add("-I");
args.add("first\"");
args.add("-i");
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);
// ... Run the process below
At first glance, you might think this is identical to the above line (that's certainly what my naive self thought). In fact, if I just print args out with spaces in between each one, the resulting strings are identical and run perfectly if directly copy and pasted to the terminal. However, for whatever reason, the program won't work as I got the message (from Weka) Quote parse error. I tried googling and found this question about how ProcessBuilder adds extra quotes to the command line (this led me to try numerous combinations of escape sequences, all of which did not work), and read this article about how ProcessBuilder/Runtime.exec() work (I tried both ProcessBuilder and Runtime.exec(), and ultimately the same problem persisted), but couldn't find anything relevant to what I needed. Weka already had bad documentation, and then their Wikispace page went down a couple weeks ago due to Wikispaces shutting down, so I have found very little info on the Weka side.
My question then is this: Is there a way to get something like the second example I put above to run such that I can group arguments together for much larger commands? I understand it may require some funky escape sequences (or maybe not?), or perhaps something else I have not considered. Any help here is much appreciated.
Edit: I updated the question to hopefully give more insight into what my problem is.
You don't need to group arguments together. It doesn't even work, as you've already noted. Take a look what happens when I call my Java programm like this:
java -jar Test.jar -i -s "-t 500"
This is my "program":
public class Test {
public static void main(String[] args) {
for( String arg : args ) {
System.out.println(arg);
}
}
}
And this is the output:
-i
-s
-t 500
The quotes are not included in the arguments, they are used to group the arguments. So when you pass the arguments to the ProcessBuilder like you did, it is essentially like you'd written them with quotes on the command line and they are treated as a single argument, which confuses the parser.
The quotes are only necessary when you have nested components, e.g. FilteredClassifier. Maybe my answer on another Weka question can help you with those nested components. (I recently changed the links to their wiki to point to the Google cache until they established a new wiki.)
Since you didn't specify what case exactly caused you to think about grouping, you could try to get a working command line for Weka and then use that one as input for a program like mine. You can then see how you would need to pass them to a ProcessBuilder.
For your example I'd guess the following will work:
List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp");
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("weka.filters.unsupervised.attribute.ClusterMembership -I first");
args.add("-i");
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);
Additional details
What happens inside Weka is basically the following: The options from the arguments are first processed by weka.filters.Filter, then all non-general filter options are processed by weka.filters.MultiFilter, which contains the following code in setOptions(...):
filters = new Vector<Filter>();
while ((tmpStr = Utils.getOption("F", options)).length() != 0) {
options2 = Utils.splitOptions(tmpStr);
filter = options2[0];
options2[0] = "";
filters.add((Filter) Utils.forName(Filter.class, filter, options2));
}
Here, tmpStr is the value for the -F option and will be processed by Utils.splitOption(tmpStr) (source code). There, all the quoting and unquoting magic happens, so that the next component will receive an options array that looks just like it would look if it was a first-level component.
Executing the gradle application plugin's installDist task creates a directory build/install/my-application-name/bin that contains wrapper scripts, my-application-name and my-application-name.bat. Running either of these scripts runs the application, and arguments passed to these scripts are passed to the underlying application.
In UNIX shell scripts you can access the name that was used to execute the program as $0. In fact, the UNIX version of the gradle-generated startup script uses $0 several times.
How can I configure the gradle application plugin such that these scripts will pass the value of $0 (and whatever the Windows equivalent is on Windows) into the underlying application, perhaps as a Java system property?
Since parameter for obtaining the name of the script being run is referenced differently in Linux($0) and in Windows(%0), the most straightforward way to generate custom scripts would be to use separate custom templates for the respective start script generators:
startScripts {
unixStartScriptGenerator.template = resources.text.fromFile('unixStartScript.txt')
windowsStartScriptGenerator.template = resources.text.fromFile('windowsStartScript.txt')
}
The default templates are easy to obtain invoking e.g. unixStartScriptGenerator.template.asString()
Documentation on customizing the start scripts can be found here.
This is what I ended up doing, based on jihor's answer. I'm posting it here just so that there's a working answer for anyone else interested:
startScripts {
def gen = unixStartScriptGenerator
gen.template = resources.text.fromString(
gen.template.asString().replaceFirst('(?=\nDEFAULT_JVM_OPTS=.*?\n)') {
'\nJAVA_OPTS="\\$JAVA_OPTS "\'"-Dprogname=\\$0"\''
})
// TODO: do something similar for windowsStartScriptGenerator
}
This uses replaceFirst is instead of replace so we can match a pattern. This is a little less brittle, and also lets us use lookahead so we don't have to actually replace what we're looking for. (This is groovy's variant of replaceFirst that takes a closure, by the way. This requires far less escaping than the version that takes a replacement string in this case.)
Also, instead of:
JAVA_OPTS="$JAVA_OPTS -Dprogname=$0"
we actually need something like:
JAVA_OPTS="$JAVA_OPTS "'"-Dprogname=$0"'
This is because $0 may contains special character (like spaces), and the startup script removes one level of quoting in the value of $JAVA_OPTS using eval set --.
(If anyone knows how to make this work on Windows, pleas feel free to update this answer.)
I took an alternative approach. According to the documentation, as far back as Gradle 2.4 and all the way through Gradle 4.8, we should be able to set the following properties within the startScripts task:
applicationName
optsEnvironmentVar
exitEnvironmentVar
mainClassName
executableDir
defaultJvmOpts
appNameSystemProperty
appHomeRelativePath
classpath
Unfortunately, this is not true for the following properties, which seem to have never been exposed:
appNameSystemProperty
appHomeRelativePath
If appNameSystemProperty were exposed as the documentation describes, then we should be able to simply do the following:
startScripts {
applicationName = 'foo'
appNameSystemProperty = 'appName'
}
This would then result in the addition of -DappName=foo to the Java command constructed within both of the start scripts.
Since this is not the case, I took the following approach, which is a bit more verbose than the earlier solution to this question, but is perhaps less brittle because it does not rely on tweaking the out-of-box templates. Instead, it results in the documented behavior.
startScripts {
mainClassName = '...'
applicationName = 'foo'
unixStartScriptGenerator =
new CustomStartScriptGenerator(generator: unixStartScriptGenerator)
windowsStartScriptGenerator =
new CustomStartScriptGenerator(generator: windowsStartScriptGenerator)
}
class CustomStartScriptGenerator implements ScriptGenerator {
#Delegate
ScriptGenerator generator
void generateScript(JavaAppStartScriptGenerationDetails details,
Writer destination) {
details = new CustomDetails(details: details)
this.generator.generateScript(details, destination)
}
static class CustomDetails implements JavaAppStartScriptGenerationDetails {
#Delegate
JavaAppStartScriptGenerationDetails details
#Override
String getAppNameSystemProperty() { 'appName' }
}
}
I know I can use "code" in GitHub Flavored Markdown to highlight a code snippet. But I am not able to display line numbers for a snippet. Is there a way to do so?
```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
I want a line number to be put at the beginning of each line, like this:
1 var s = "JavaScript syntax highlighting";
2 alert(s);
As you may noticed in Markdown Cheatsheet, GitHub does not show line numbers in code blocks.
As a hack, you could save a pic of your code at https://carbon.now.sh and post it; they support line numbers as an option.
You can get something similar that you need using awk '{printf("% 4d %s\n", NR, $0)}' StartDsl.scala where StartDsl.scala is your source code file. Paste the result between
```scala
<your code here>
```
Although it is not available in GitHub as the question asks, I have discovered today if you add an = sign after the opening line, on some Markdown editors, it gives the desired result.
eg:
```javascript=
var s = "JavaScript syntax highlighting";
alert(s);
```
This works on Markdown editors such as HackMD
See your example on HackMD
So, you will need to help yourself by adding css to your html page. As a code goes into <pre> </pre> block in markdown.
You could apply your logic to this block to put line number against each line.
See https://codepen.io/heiswayi/pen/jyKYyg for reference.
I use RStudio with RMarkdown to render my Markdown (.md) files. It works great. Using the RMarkdown, the specification is made this way:
```{.javascript .numberLines .lineAnchors}
var s = "JavaScript syntax highlighting";
alert(s);
```
Yes, there are many markdown editors available and I'm not sure that this will work with all the editors, but RStudio/RMarkdown is a really great tool, which I use since a long time ago (IMHO).
Just add an = after the language you choose !
```java=
java code exemple:
int i = 5
```java=
Now here is the solution for adding line numbers in Markdown.
https://shd101wyy.github.io/markdown-preview-enhanced/#/markdown-basics?id=line-numbers
You can enable line number for a code block by adding line-numbers
class.
I am using the Jenkins console sections plugin [1] on a windows server. It is excellent in order to make a nice left navbar on my logs.
Positively, I would like any error message to cause a section header, eg;
Assert-PathExstsNotTooLong : ERROR, The path does not exist: E:\P...
...
Oops! Error, please do not do that.
Negatively, I would like to be able to avoid having spelled-out execution templates cause a new section header, eg the below.
[workspace] $ cmd.exe /C " c:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe /p:Configuration=Debug /p:VisualStudioVersion=12.0 "E:\Program Files (x86)\Jenkins\jobs\M.sln"
Using references here on SO [2] and on the tester you recommended [3], I came up with the following, but it is not working?
^(?=(.*([Ee][Rr][Rr][Oo][Rr] ).*))(?!(%%ERRORLEVEL%%))
Using Regex101's amazing tester, with JS flavor, I used the above as input and had these test strings and outputs. The second line of match info perhaps explains my issue but I do not understand it.
test-strings =
help error you should see me
i am %%errorlevel%% again
i am not a section
match-info;
1. `help error you should see me`
2. `error `
Any tips?
thank you!
1.[] ;This plugin uses Java Regex, per its docs ; ; ; ; X.Collapsing Console Sections Plugin - Jenkins - Jenkins Wiki ; ; https://wiki.jenkins-ci.org/display/JENKINS/Collapsing+Console+Sections+Plugin
2.[] ; An example regex on characters, not strings, to avoid; ; ; ; X.java - Regular expression include and exclude special characters - Stack Overflow ; ; Regular expression include and exclude special characters
3.[] ; ; ; ; ; X.Online regex tester and debugger: JavaScript, Python, PHP, and PCRE ; ; https://www.regex101.com/#javascript
(I can't add comments yet, otherwise I'd ask directly, but your example of a spelled-out message template doesn't include the text %%ERRORLEVEL%%, but I assume that it's meant to be a string with %%ERRORLEVEL%% somewhere in the middle of it. Also, as the example isn't quite right, I can't tell exactly what you mean by "not working")
Your problem is that your regex matches ERROR_ (with a space) anywhere in the text, except where the text is exactly %%ERRORLEVEL%%. I think that instead you could write:
^(?=(.*([Ee][Rr][Rr][Oo][Rr])))(?!.*(%%ERRORLEVEL%%)).*
Do you really need to only match ERROR_ (with a space) as opposed to ERROR (whether or not it has a space)? If the former, then you are already excluding %%ERRORLEVEL%%, and you could just use .*(?i:ERROR ).* as the full regex.
The Collapsing Console Sections Plugin uses Java regular expressions, so you can use (?i:ERROR) to match ERROR case-insensitively.
You need a trailing .* before and after your negative-lookahead atom for %%ERRORLEVEL%%, otherwise it will only exclude an exact match
The documentation for the plugin doesn't say whether the pattern has to match a line completely, or if it just matches text within the line. If it matches the line completely, the leading ^ is unnecessary, but won't be doing any harm.
You've got capturing brackets around ERROR and %%ERRORLEVEL%%. If you're not doing anything with that text, then those brackets are unnecessary.
The following regex will match any line with any of ERROR, Error, error etc in it, except lines with any of %%ERRORLEVEL%%, %%ErrorLevel%%, %%errorlevel%% etc.
^(?=.*(?i:ERROR))(?!.*(?i:%%ERRORLEVEL%%)).*
My requirement is simple. At the beginning of each file there should be a block comment like this:
/*
* This file was last modified by {username} at {date} and has revision number {revisionnumber}
*/
I want to populate the {username}, {date} and {revisionnumber} with the appropriate content from SVN.
How can I achieve this with NetBeans and Subversion? I have searched a lot but I can't find exactly what I need.
I looked at this question and got some useful information. It is not exactly duplicate because I am working with NetBeans but the idea is the same. This is my header:
/*
* $LastChangedDate$
* $LastChangedRevision$
*/
Then I go to Team > Subversion > Svn properties and add svn:keywords as property name and LastChangedDate LastChangedRevision as property value.
And when I commit from NetBeans it looks like this:
/*
* $LastChangedDate: 2012-02-13 17:38:57 +0200 (Пн, 13 II 2012) $
* $LastChangedRevision: 27 $
*/
Thanks all for the support! I will accept my answer because other answers do not include the NetBeans information. Nevertheless I give +1 to the other answers.
As this data only exists after the file was committed it should be set by SVN itself, not a client program. (And client-side processing tends to get disabled or not configured at all.) This means there is no simple template/substitute like you want, because then after the first replacement the template variables would be lost.
You can find information abut SVN's keyword substitution here. Then things like $Rev$ can be replaced by $Rev: 12 $.
You can do this with The SubWCRev Program.
SubWCRev is Windows console program which can be used to read the
status of a Subversion working copy and optionally perform keyword
substitution in a template file. This is often used as part of the
build process as a means of incorporating working copy information
into the object you are building. Typically it might be used to
include the revision number in an “About” box.
This is typically done during the build process.
If you use Linux, you can find a Linux binary here. If you wish, you could also write your own using the output of svn log.
I followed Petar Minchev's suggestions, only I put the $LastChangedRevision$ tag not in a comment block but embedded it in a string. Now it is available to programmatically display the revision number in a Help -> About dialog.
String build = "$LastChangedRevision$";
I can later display the revision value in the about dialog using a String that has all of the fluff trimmed off.
String version = build.replace("$LastChangedRevision:", "").replace("$", "").trim();
I recommend a slightly different approach.
Put the following header at the top of your source files.
/*
* This file was last modified by {username} at {date} and has revision number {revisionnumber}
*/
Then add a shell script like this
post update, checkout script
USERNAME=# // use svnversion to get username
DATE=# // use svnversion to get revisio nnumber
sed -e "s#{username}#${USERNAME}#" -e "s#{date}#${DATE}#" ${SOURCE_CONTROL_FILE} > ${SOURCE_FILE}
pre commit script
cat standard_header.txt > ${SOURCE_CONTROL_FILE}
tail --lines $((${LENGTH}-4)) ${SOURCE_FILE} >> ${SOURCE_CONTROL_FILE}