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