1

I have a Java application for Windows. I would like that Java application, when run, to create a new file on disk with a file name that includes a version number. That version number is available as an OS environment variable while the Java application is being built by my Azure Dev Ops (ADO) pipeline. The ADO pipeline builds my code using CMake (which runs the javac command).

A solution is given here when using Maven as the build system. I could not find how to do the same in my build system, which does not use Maven and pom.xml. Is there a way to do the same when using javac command line?

Thanks,

Darren

Darren Cohen
  • 126
  • 6
  • ADO can build with Maven. Visual Studio supports Maven. Why use a platform specific tool that very few projects will use? – stdunbar Apr 21 '21 at 01:35
  • CMake runs on many platforms, that's why we use it in our ADO pipeline. This Java code builds for Linux, Android, iOS and Windows. The change I want to make (discussed above) however only applies to the Widnows build of this code. – Darren Cohen Apr 21 '21 at 01:41
  • Access Windows OS environment variable? – Holinc Apr 21 '21 at 01:52
  • Essentially you have to capture the value of the variable at compile time. I know nothing of your build system, but I'd suggest writing the version number to a file, then including the file in a jar as a resource which will then be available at the time the jar is run. – user15187356 Apr 21 '21 at 01:53
  • @Darren - I'm not saying it doesn't work. An assembly language program would work with enough effort. But if you're in the Java world CMAKE is not one of the normal build tools. Going against the flow is sometimes useful but there are many, many build tools and I'd just encourage using something that, in the Java world, is a bit more standard. – stdunbar Apr 21 '21 at 02:32
  • Thanks @user15187356 for your suggestion! – Darren Cohen Apr 21 '21 at 15:11
  • I slightly edited my question to write "OS environment variable" instead of just "environment variable", and remove the reference to Vistual Studio as it is not relevant to this question. Thanks @Holinc. – Darren Cohen Apr 21 '21 at 15:13
  • @stdubar thank you for your comment. This Java code is part of much larger code base that also compiles C++ and C# code, hence the decision to use CMake as the single build infrastructure for the whole project. CMake is both cross platform and cross programming languages. I understand the advantages of using Maven for Java only code, and we do use it in other projects. – Darren Cohen Apr 21 '21 at 15:21

1 Answers1

1

CMake runs on many platforms

maven runs on about just as many, though.

That version number is available as an environment variable while the Java application is being built

javac has absolutely no support for preprocessing and cannot do this, period. preprocessing and IDEs mostly hate each others guts, which probably explains the java ecosystem and communities extremely strong dislike of the notion of a preprocessor; they like IDEs.

Answering your question directly

Thus, you'll have to look elsewhere. For example, you can include some token value in your java source file, such as:

private static final String FULL_VERSION_ID = "{@@ INJECT_VERSION_HERE @@}";

and then, before invoking javac, use sed or some other search and replace tool. However, this complicates your build considerably: You'd have to take that source file (which is really no longer an actual source file, it's a template to a source file now), copy it over (applying the sed transformation on the fly), and then compile that. That means the source file has to live elsewhere or have a different extension and that plays absolute havoc with your editor, because this is not 'normal' in the java community, so no tools support this sort of thing. Your IDE will get confused and show all sorts of errors in your code due to the (to the IDE) missing file.

Another solution is some creative hackery, where sed will modify the file in flight:

private static final String FULL_VERSION_TEMPLATE = "{@@ INJECT_VERSION_HERE @@}1.12";
private static final String FULL_VERSION = 
  FULL_VERSION_TEMPLATE.substring(FULL_VERSION_TEMPLATE.indexOf("@@}") + 3);

And then your sed tool can just 'replace on the spot', using a regexp that will find the whole thing, which leaves the {@@ @@} marker intact, and which just replaces the 1.12 (everything from after the marker up to the first quotation mark - and then you need a rule that versions cannot ever contain quotation marks. I hope you can make that promise or this gets even more complicated.

CMake runs off of file modification timestamps, right? Oof. Well, try to make it leave the late 80s and work off of hashes instead, or this is going to cause needless recompilation. Alternatively, try to make sed not update the time stamp if the requested change ends up being a no-op.

But wait...

Are you sure you want to go down that deep, dark rabbit hole? Can't you do this much more java-esque, much simpler thing?

Instead of modifying a source file, can you instead add 1 non-class file to the end result (I assume your CMake tool ends up making a dex or jar or something similar)? For example, ensure that version.txt is available in the 'root' of the jar file. In java, you can write code that goes: "Hey, look in the exact same place you're looking for the very class files that compose this very application, and gimme the contents of one of em":

public class MyApp {
  public static void main(String[] args) {
    System.out.println(readVersion());
  }
  public static String readVersion() {
    try {
      try (var in = MyApp.class.getResourceAsStream("/version.txt")) {
        return new String(in.readAllBytes(), StandardCharsets.UTF_8).trim();
      }
    } catch (IOException e) {
      throw new InternalError("version.txt not found");
    }
  }
}

That'll do the job. This seems much easier: Now your CMake script just needs to make sure version.txt has the right version in it and is included in the jar or whatever the output of your build pipeline is (I admit I'm not familiar with what ADO is).

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Thank you @rzwitserloot for the very detailed reply! This is very helpful, and I appreciate the effort you put in to answering my question. I like the idea of inserting a version.txt file and will look into it today. – Darren Cohen Apr 21 '21 at 15:17