1 /* 2 * Copyright (c) 2012-2020 The ANTLR Project. All rights reserved. 3 * Use of this file is governed by the BSD 3-clause license that 4 * can be found in the LICENSE.txt file in the project root. 5 */ 6 7 module antlr.v4.runtime.RuntimeMetaData; 8 9 import std.algorithm : min; 10 import std.stdio : stderr; 11 import std.string : indexOf; 12 13 /** 14 * This class provides access to the current version of the ANTLR 4 runtime 15 * library as compile-time and runtime constants, along with methods for 16 * checking for matching version numbers and notifying listeners in the case 17 * where a version mismatch is detected. 18 * 19 * <p> 20 * The runtime version information is provided by {@link #VERSION} and 21 * {@link #getRuntimeVersion()}. Detailed information about these values is 22 * provided in the documentation for each member.</p> 23 * 24 * <p> 25 * The runtime version check is implemented by {@link #checkVersion}. Detailed 26 * information about incorporating this call into user code, as well as its use 27 * in generated code, is provided in the documentation for the method.</p> 28 * 29 * <p> 30 * Version strings x.y and x.y.z are considered "compatible" and no error 31 * would be generated. Likewise, version strings x.y-SNAPSHOT and x.y.z are 32 * considered "compatible" because the major and minor components x.y 33 * are the same in each.</p> 34 * 35 * <p> 36 * To trap any error messages issued by this code, use System.setErr() 37 * in your main() startup code. 38 * </p> 39 */ 40 class RuntimeMetaData 41 { 42 43 enum string VERSION = "4.8.0"; 44 45 /** 46 * This method provides the ability to detect mismatches between the version 47 * of ANTLR 4 used to generate a parser, the version of the ANTLR runtime a 48 * parser was compiled against, and the version of the ANTLR runtime which 49 * is currently executing. 50 * 51 * <p> 52 * The version check is designed to detect the following two specific 53 * scenarios.</p> 54 * 55 * <ul> 56 * <li>The ANTLR Tool version used for code generation does not match the 57 * currently executing runtime version.</li> 58 * <li>The ANTLR Runtime version referenced at the time a parser was 59 * compiled does not match the currently executing runtime version.</li> 60 * </ul> 61 * 62 * <p> 63 * Starting with ANTLR 4.3, the code generator emits a call to this method 64 * using two constants in each generated lexer and parser: a hard-coded 65 * constant indicating the version of the tool used to generate the parser 66 * and a reference to the compile-time constant {@link #VERSION}. At 67 * runtime, this method is called during the initialization of the generated 68 * parser to detect mismatched versions, and notify the registered listeners 69 * prior to creating instances of the parser.</p> 70 * 71 * <p> 72 * This method does not perform any detection or filtering of semantic 73 * changes between tool and runtime versions. It simply checks for a 74 * version match and emits an error to stderr if a difference 75 * is detected.</p> 76 * 77 * <p> 78 * Note that some breaking changes between releases could result in other 79 * types of runtime exceptions, such as a {@link LinkageError}, prior to 80 * calling this method. In these cases, the underlying version mismatch will 81 * not be reported here. This method is primarily intended to 82 * notify users of potential semantic changes between releases that do not 83 * result in binary compatibility problems which would be detected by the 84 * class loader. As with semantic changes, changes that break binary 85 * compatibility between releases are mentioned in the release notes 86 * accompanying the affected release.</p> 87 * 88 * <p> 89 * <strong>Additional note for target developers:</strong> The version check 90 * implemented by this class is designed to address specific compatibility 91 * concerns that may arise during the execution of Java applications. Other 92 * targets should consider the implementation of this method in the context 93 * of that target's known execution environment, which may or may not 94 * resemble the design provided for the Java target.</p> 95 * 96 * @param generatingToolVersion The version of the tool used to generate a parser. 97 * This value may be null when called from user code that was not generated 98 * by, and does not reference, the ANTLR 4 Tool itself. 99 * @param compileTimeVersion The version of the runtime the parser was 100 * compiled against. This should always be passed using a direct reference 101 * to {@link #VERSION}. 102 */ 103 public static void checkVersion(string generatingToolVersion, string compileTimeVersion) 104 { 105 string runtimeVersion = VERSION; 106 bool runtimeConflictsWithGeneratingTool = false; 107 bool runtimeConflictsWithCompileTimeTool = false; 108 109 if (generatingToolVersion) { 110 runtimeConflictsWithGeneratingTool = 111 (runtimeVersion != generatingToolVersion) && 112 (getMajorMinorVersion(runtimeVersion) != getMajorMinorVersion(generatingToolVersion)); 113 } 114 115 runtimeConflictsWithCompileTimeTool = 116 (runtimeVersion != compileTimeVersion) && 117 (getMajorMinorVersion(runtimeVersion) != getMajorMinorVersion(compileTimeVersion)); 118 119 if (runtimeConflictsWithGeneratingTool) { 120 stderr.writefln("ANTLR Tool version %s used for code generation does not match the current runtime version %s", 121 generatingToolVersion, runtimeVersion); 122 } 123 if ( runtimeConflictsWithCompileTimeTool ) { 124 stderr.writefln("ANTLR Runtime version %s used for parser compilation does not match the current runtime version %s", 125 compileTimeVersion, runtimeVersion); 126 } 127 128 } 129 130 /** 131 * Gets the major and minor version numbers from a version string. For 132 * details about the syntax of the input {@code version}. 133 * E.g., from x.y.z return x.y. 134 * 135 * @param version The complete version string. 136 * @return A string of the form <em>major</em>.<em>minor</em> containing 137 * only the major and minor components of the version string. 138 */ 139 public static string getMajorMinorVersion(string antlr_version) 140 { 141 size_t firstDot = antlr_version.indexOf('.'); 142 size_t secondDot = firstDot >= 0 ? antlr_version.indexOf('.', firstDot + 1) : -1; 143 size_t firstDash = antlr_version.indexOf('-'); 144 size_t referenceLength = antlr_version.length; 145 if (secondDot >= 0) { 146 referenceLength = min(referenceLength, secondDot); 147 } 148 149 if (firstDash >= 0) { 150 referenceLength = min(referenceLength, firstDash); 151 } 152 153 return antlr_version[0..referenceLength]; 154 } 155 156 } 157 158 version(unittest) { 159 160 import dshould : be, equal, not, should; 161 import std.typecons : tuple; 162 import unit_threaded; 163 164 class Test { 165 166 @Tags("mt", "reg") 167 @("compareMetaDataVersion") 168 unittest { 169 "4.8".should.equal(RuntimeMetaData.getMajorMinorVersion(RuntimeMetaData.VERSION)); 170 RuntimeMetaData.checkVersion("4.8", "4.8.0"); 171 } 172 } 173 }