Scripts: verify compiler definitions of native and managed (#4675)
authorMyungJoo Ham <myungjoo.ham@gmail.com>
Thu, 23 Jun 2016 11:13:46 +0000 (20:13 +0900)
committerJan Vorlicek <janvorli@microsoft.com>
Thu, 23 Jun 2016 11:13:46 +0000 (13:13 +0200)
* Scripts: find out compiler definitions of CMake

In order to find mismatch between native and managed,
we need to know the list of definitions of native.

The copmiler definitions are stored at cmake.definitions

This addresses the complaints of #4674

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
* Scripts: add check-definitions.py

scripts/check-definitions.py checks the consistency between
the native-build (CMake) compiler definitions and
the managed-build (MSBuild/mscorlib) compiler definitions
at build-time and prints out potentially dangerous
inconsistencies.

In order to get the proper results, managed build should
be executed after the native build (build.sh will do so
if no options such as skipnative or skipmanaged are given.)

Fix #4674

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
* Scripts: allow check-definitions py to ignore the harmless

The third argument of check-definitions.py specifies harmless keywords
to be suppressed from emitting warning messages.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
* Scripts: add ignored cdefine keywords for warning

As an example of how to declare compiler definition keywords
that are harmless to be inconsistent between the native and the
managed, we have added FEATURE_IMPLICIT_TLS and FEATURE_HIJACK.

Developers may add more keywords in System.Private.CoreLib.csproj
if the keywords are verified to be harmless; i.e., although
the keywords exist in both cmake and clr.coreclr.props,
the keywords are NEVER used in either side of the sources
or the keywords only happen to have the same name while they
denote the completely different semantics and may be disjoint.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
CMakeLists.txt
definitionsconsistencycheck.cmake [new file with mode: 0644]
src/mscorlib/System.Private.CoreLib.csproj
src/scripts/check-definitions.py [new file with mode: 0644]

index f105717..539a129 100644 (file)
@@ -574,3 +574,5 @@ if(NOT DEFINED CLR_CROSS_COMPONENTS_BUILD AND CLR_CROSS_COMPONENTS_BUILD_ENABLED
     INSTALL_DIR $ENV{__CMakeBinDir}/${CLR_CROSS_BUILD_HOST_ARCH}
   )
 endif()
+
+include(definitionsconsistencycheck.cmake)
diff --git a/definitionsconsistencycheck.cmake b/definitionsconsistencycheck.cmake
new file mode 100644 (file)
index 0000000..cc029bb
--- /dev/null
@@ -0,0 +1,11 @@
+get_directory_property( DirDefs COMPILE_DEFINITIONS )
+
+# Reset the definition file
+file(WRITE cmake.definitions "")
+foreach( d ${DirDefs} )
+    if($ENV{VERBOSE})
+        message( STATUS "Compiler Definition: " ${d} )
+    endif($ENV{VERBOSE})
+    file(APPEND cmake.definitions ${d})
+    file(APPEND cmake.definitions "\n")
+endforeach()
index 0bb3377..5535c89 100644 (file)
       <ResGenDefines>$(DefineConstants)</ResGenDefines>
     </SplitTextStringResource>
   </ItemGroup>
+
+  <Target Name="CDefineChecker" BeforeTargets="Build">
+    <!-- Compiler Definition Verification -->
+    <Message Importance="High" Text="============" />
+    <PropertyGroup>
+      <IgnoreDefineConstants>FEATURE_IMPLICIT_TLS;FEATURE_HIJACK</IgnoreDefineConstants>
+    </PropertyGroup>
+    <Exec Command='python $(MSBuildThisFileDirectory)../scripts/check-definitions.py $(MSBuildThisFileDirectory)../../cmake.definitions "$(DefineConstants)" "$(IgnoreDefineConstants)" ' />
+    <Message Importance="High" Text="============" />
+  </Target>
+
   <ItemGroup>
     <EmbeddedResource Include="$(NlpObjDir)\charinfo.nlp">
       <LogicalName>charinfo.nlp</LogicalName>
diff --git a/src/scripts/check-definitions.py b/src/scripts/check-definitions.py
new file mode 100644 (file)
index 0000000..19d4739
--- /dev/null
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+#
+# check-definitions.py
+#  This script checks the consistency between compiler definitions
+# of the native part of CoreCLR and managed part (mscorlib.dll) of
+# CoreCLR
+#
+# Usage:
+#   $ ./check-definitions.py Definition_File String_of_Definitions [String_of_Ignored_Definitions]
+#
+#      Definition_File: the filename of a file containing the list of
+#          compiler definitions of CMAKE, seperated by line.
+#          (Mandatory)
+#      String_of_Definitions: the list of managed code compiler
+#          definitions, seperated by semicolon without spaces.
+#          (Mandatory)
+#      String_of_Ignored_Definitions: the list of compiler definitions
+#          to be suppressed from emitting warnings, seperated by semicolon without spaces.
+#          (Optional)
+#
+# (c) 2016 MyungJoo Ham <myungjoo.ham@samsung.com>
+
+import sys
+import re
+
+debug = 0
+
+# For the native part, return the sorted definition array.
+def loadDefinitionFile(filename):
+    result = []
+    f = open(filename, 'r')
+    for line in f:
+        theLine = line.rstrip("\r\n").strip()
+        if (len(theLine) > 0):
+            result.append(theLine)
+
+    f.close()
+    result = sorted(result)
+    return result
+
+
+# For the managed part, return the sorted definition array.
+def loadDefinitionString(string):
+    splitted = string.split(';')
+    result = []
+    for line in splitted:
+       theLine = line.strip()
+       if (len(theLine) > 0):
+           result.append(theLine)
+
+    result = sorted(result)
+    return result
+
+
+def getDiff(arrNative, arrManaged):
+    result = [[], []]
+    iF = 0 # From file (native)
+    nF = len(arrNative)
+
+    iS = 0 # From string (managed)
+    nS = len(arrManaged)
+
+    while (iS < nS) and (iF < nF):
+        if (arrNative[iF] == arrManaged[iS]):
+            if (debug == 1):
+                print("Both have " + arrNative[iF])
+            iF = iF + 1
+            iS = iS + 1
+        elif (arrNative[iF] == (arrManaged[iS] + "=1")):
+            if (debug == 1):
+                print("Both have " + arrNative[iF] + "(=1)")
+            iF = iF + 1
+            iS = iS + 1
+        elif (arrNative[iF] < arrManaged[iS]):
+            if (debug == 1):
+                print("--- Managed Omitted " + arrNative[iF])
+            result[1].append(arrNative[iF])
+            iF = iF + 1
+        elif (arrNative[iF] > arrManaged[iS]):
+            if (debug == 1):
+                print("+++ Managed Added " + arrManaged[iS])
+            result[0].append(arrManaged[iS])
+            iS = iS + 1
+
+    if (iS < nS):
+        while iS < nS:
+            if (debug == 1):
+                print("+++ Managed Added " + arrManaged[iS])
+            result[0].append(arrManaged[iS])
+            iS = iS + 1
+    elif (iF < nF):
+        while iF < nF:
+            if (debug == 1):
+                print("--- Managed Omitted " + arrNative[iF])
+            result[1].append(arrNative[iF])
+            iF = iF + 1
+    return result
+
+
+def printPotentiallyCritical(arrDefinitions, referencedFilename, arrIgnore):
+    f = open(referencedFilename, 'r')
+    content = f.read()
+    f.close()
+    for keyword in arrDefinitions:
+        skip = 0
+
+        if (keyword[-2:] == "=1"):
+            key = keyword[:-2]
+        else:
+            key = keyword
+
+        if re.search("[^\\w]"+key+"[^\\w]", content):
+            for ign in arrIgnore:
+                if key == ign:
+                    skip = 1
+                    break
+            if skip == 0:
+                print(keyword)
+
+# MAIN SCRIPT
+if len(sys.argv) < 3:
+    print "\nUsage:"
+    print "$ check-definitions.py [Definition file] [String of definitions]"
+    print "    Definition file contains the list of cmake (native) compiler definitions"
+    print "      seperated by line."
+    print "    String of definitions contains the list of csproj (managed) definitions"
+    print "      seperated by semicolons."
+    sys.exit(-1)
+
+filename = sys.argv[1]
+string = sys.argv[2]
+
+arrayNative = loadDefinitionFile(filename)
+arrayManaged = loadDefinitionString(string)
+arrayIgnore = []
+
+if len(sys.argv) > 3:
+    arrayIgnore = loadDefinitionString(sys.argv[3])
+
+arrays = getDiff(arrayNative, arrayManaged)
+# arrays[0] = array of added in managed
+# arrays[1] = array of omitted in managed (added in native)
+
+print "Potentially Dangerous Compiler Definitions in clrdefinitions.cmake (omitted in native build):"
+printPotentiallyCritical(arrays[0], "../../clrdefinitions.cmake", arrayIgnore)
+
+print "Potentially Dangerous Compiler Definitions in clr.defines.targets (omitted in managed build):"
+printPotentiallyCritical(arrays[1], "../../clr.defines.targets", arrayIgnore)
+
+print "Definition Check Completed."
+