Javac equivalent to "-D"?

Is there a way to give the java compiler some kind of variable available to run java code?
In C / C ++, I can give a compilation -DKEY=VALUE , and this will cause the preprocessor to have #define for KEY equal to VALUE. I can then check this value at compile time to compile the code.
I found java -D, but at the same time the values ​​give the java command line in System.getProperty() . I want the argument to indicate at compile time, not at the time of the call.

+7
java c ++
source share
5 answers

With java annotations, you can generate additional code on the fly, which you can configure on the command line. This allows you to create more source code, configuration files, xml files ... The main limitation is that you are only allowed to (re) generate new source files, you cannot modify existing ones.

The following is a short tutorial on how to allow the javac command to specify parameters that will be displayed in Java code. How useful is this? I.e. you can specify a logical option that will disable some parts of the code, I am sure that this part of the code can be removed using tools such as proguard, or even optimized using javac. For other purposes, the version number is indicated. These usage examples are mainly used for using C ++ marcros.

So you need to:

  • A dummy annotation class that allows the processor to work. It should be indicated only once in your application.
  • the processor class that will be executed on the dummy annotation and generate the options class. It will also read options from javac command line.
  • dummy main class for testing purposes.

You will also have to compile your processor file before compiling the main class. This, of course, is only done by changing the processor class. All three files are at the bottom. Now the compilation is as follows (I'm on windows):

Compile processor:

 javac .\com\example\ConfigWritterAnnotationProcessor.java 

Then Main.java with additional parameters for the processor:

 javac -processor com.example.ConfigWritterAnnotationProcessor -AtextToPrint="Hello World!" -AenablePrint=true ./com/example/Main.java 

And that’s it, now you can run Main.class, and it will use the Options class generated at compile time with the above parameters. It will look like this:

 package com.example; public class Options { public static final String textToPrint = "Hello World!"; public static final boolean enablePrint = true; } 

ProcessorStarterAnnotation.java

 package com.example; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE}) public @interface ProcessorStarterAnnotation { } 

Main.java

 package com.example; @ProcessorStarterAnnotation public class Main { public static void main(String[] args) { if ( com.example.Options.enablePrint ) { System.out.println(com.example.Options.textToPrint); } else { System.out.println("Print disabled"); } } } 

ConfigWritterAnnotationProcessor.java

 package com.example; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.util.Map; import java.util.Set; @SupportedAnnotationTypes("com.example.ProcessorStarterAnnotation") @SupportedSourceVersion(SourceVersion.RELEASE_6) @SupportedOptions({"textToPrint", "enablePrint"}) public class ConfigWritterAnnotationProcessor extends AbstractProcessor { private Map<String,String> options; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); options = processingEnv.getOptions(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment currentRound) { if (!currentRound.processingOver()) { // This for-s are because processor is also run on newly created Options class. for (TypeElement te : annotations) { for (Element e : currentRound.getElementsAnnotatedWith(te)) { try { processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Creating com.example.Options"); JavaFileObject javaFile = processingEnv.getFiler().createSourceFile("com.example.Options"); Writer w = javaFile.openWriter(); try { PrintWriter pw = new PrintWriter(w); pw.println("package com.example;"); pw.println("public class Options {"); pw.println(" public static final String textToPrint = \"" + options.get("textToPrint") + "\";"); pw.println(" public static final boolean enablePrint = " + options.get("enablePrint") + ";"); pw.println("}"); pw.flush(); } finally { w.close(); } } catch (IOException x) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, x.toString()); } } } } return false; } } 
+7
source share

javac has

 -Akey[=value] 

command line parameter for passing information to annotation processors .

+10
source share

There is nothing like that in Java. Compile-time constants must be declared in the source code, and as far as I know, there is no preprocessor.

By the way, you can use the flags specified on the java command line (-D args) to initialize java constants at run time, which will mimic what you are looking for.

Example:

 class Foo { private static final String BAR; static { String foobar = System.getProperty("foo.bar"); if(foobar != null && foobar.length()>0) { BAR = foobar; } else { BAR = "somedefaultvalue"; } } } 

Call with java Xxx -Dfoo.bar=foobar

+2
source share

Since Java has no concept of preprocessing, the solution is to create your own.

You can think about using a standard C preprocessor or personalized code and compile the pre-processed output, but this has the disadvantage of duplicating files, so that the project will become more complex and support from the development environment (for example, the ability to switch to a syntax error).

My suggestion is to use annotations through comments that the user preprocessor will direct, and let it do the substitution before compilation.

For example,

  public static void main(String[] args) { int nDisks = 3; doTowers(nDisks, 'A', 'B', 'C'); } 

will become

  public static void main(String[] args) { int nDisks = /*@NDISKS*/ 3 /**/; doTowers(nDisks, 'A', 'B', 'C'); } 

Then your preprocessor will have a definition file, for example

 NDISKS 5 

turning code into

  public static void main(String[] args) { int nDisks = /*@NDISKS*/ 5 /**/; doTowers(nDisks, 'A', 'B', 'C'); } 

Similarly, you can emulate conditional code compilation with

  doTowers(topN - 1, from, to, inter); /*!PRINT*/ System.out.println("Disk " + topN + " from " + from + " to " + to); /**/ doTowers(topN - 1, inter, from, to); 

which can be converted by a preprocessor (with PRINT OFF type definition) to

  doTowers(topN - 1, from, to, inter); /*!PRINT System.out.println("Disk " + topN + " from " + from + " to " + to); */ doTowers(topN - 1, inter, from, to); 

You can use alternative syntaxes, but the main ideas

  • that the annotated code remains compiled,
  • that subprocess substitutions are reversible.
+1
source share

It would be against the design of the language to be that way. What -DKEY=VALUE does is that it actually replaces KEY with VALUE in the source during the preprocessor in C / C ++.

There is no preprocessor in Java, so the mechanism is not available. If you want something β€œequivalent,” you should ask what you mean by that. Without reprogramming the source code, it will not really be equivalent.

If, on the other hand, you would like to set the value of the KEY character to VALUE , you would run into a problem that would have to declare a KEY character in any case to determine its type. In this case, it will be only one more constant / variable with corresponding restrictions.

This means that even with such a function, it will not actually change the generated code, and you are unlikely to be better than determining the start time of a value. This is why the parameter will be supplied via java .

0
source share

All Articles