From 10d01a8379b1db9db5815d37854246b39345055b Mon Sep 17 00:00:00 2001
From: unknown <leonardo@Z3-1.redmond.corp.microsoft.com>
Date: Wed, 21 Nov 2012 22:52:52 -0800
Subject: [PATCH] Compiling java bindings on Windows

---
 scripts/mk_project.py |  2 +-
 scripts/mk_util.py    | 66 +++++++++++++++++++++++++++----------------
 scripts/update_api.py | 27 ++++++++++--------
 3 files changed, 57 insertions(+), 38 deletions(-)

diff --git a/scripts/mk_project.py b/scripts/mk_project.py
index 2f2f4a721..6d2b7c70e 100644
--- a/scripts/mk_project.py
+++ b/scripts/mk_project.py
@@ -71,7 +71,7 @@ def init_project_def():
             static=build_static_lib(),
             export_files=API_files)
     add_dot_net_dll('dotnet', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties')
-    add_java_dll('java', 'api/java', dll_name='libz3j')
+    add_java_dll('java', 'api/java', dll_name='libz3java')
     add_hlib('cpp', 'api/c++', includes2install=['z3++.h'])
     set_z3py_dir('api/python')
     # Examples
diff --git a/scripts/mk_util.py b/scripts/mk_util.py
index 34ec94626..35ef7166d 100644
--- a/scripts/mk_util.py
+++ b/scripts/mk_util.py
@@ -26,7 +26,7 @@ import string
 
 def getenv(name, default):
     try:
-        return os.environ[name]
+        return os.environ[name].strip(' "\'')
     except:
         return default
 
@@ -37,6 +37,7 @@ CXXFLAGS=getenv("CXXFLAGS", "")
 LDFLAGS=getenv("LDFLAGS", "")
 JAVA=getenv("JAVA", "java")
 JAVAC=getenv("JAVAC", "javac")
+JAVA_HOME=getenv("JAVA_HOME", None)
 
 CXX_COMPILERS=['g++', 'clang++']
 C_COMPILERS=['gcc', 'clang']
@@ -71,7 +72,9 @@ VER_BUILD=None
 VER_REVISION=None
 PREFIX='/usr'
 GMP=False
-JAVA_HOME=None
+
+def is_windows():
+   return IS_WINDOWS
 
 def unix_path2dos(path):
     return string.join(path.split('/'), '\\')
@@ -110,20 +113,25 @@ class TempFile:
         try:
             os.remove(self.name)
         except:
-            raise MKException("Failed to eliminate temporary file '%s'" % self.name)
+            pass
 
 def exec_cmd(cmd):
     if isinstance(cmd, str):
         cmd = cmd.split(' ')
     new_cmd = []
+    first = True
     for e in cmd:
-        e  = e.strip(' ')
-        if e != "":
-            se = e.split(' ')
-            if len(se) > 1:
-                new_cmd.extend(se)
-            else:
-                new_cmd.append(e)
+        if first:
+            # Allow spaces in the executable name
+	    first = False
+            new_cmd.append(e)
+        else:
+            if e != "":
+                se = e.split(' ')
+                if len(se) > 1:
+                    new_cmd.extend(se)
+                else:
+                    new_cmd.append(e)
     cmd = new_cmd
     null = open(os.devnull, 'wb')
     try:
@@ -181,18 +189,15 @@ def check_java():
     rmf('Hello.class')
     if r != 0:
         raise MKException('Failed testing Java program. Set environment variable JAVA with the path to the Java virtual machine')
-    t2 = TempFile('Z3Native.java')
-    t2.add('public final class Z3Native { public static native long mkConfig(); }\n')
-    t2.commit()
-    if is_verbose():
-        print "Testing %s (for JNI)..." % JAVAC
-    r = exec_cmd([JAVAC, 'Z3Native.java'])
-    if r != 0:
-        raise MKException('Failed testing Java compiler (for source containing JNI bindings). Set environment variable JAVAC with the path to the Java compiler')
     find_java_home()
 
 def find_java_home():
     global JAVA_HOME
+    if JAVA_HOME != None:
+        if is_verbose():
+            print "Checking jni.h..."
+        if os.path.exists('%s%sinclude%sjni.h' % (JAVA_HOME, os.sep, os.sep)):
+            return
     if is_verbose():
         print "Finding JAVA_HOME..."
     t = TempFile('output')
@@ -208,12 +213,13 @@ def find_java_home():
         m = open_pat.match(line)
         if m:
             # Remove last 3 directives from m.group(1)
+	    print m.group(1)
             tmp  = m.group(1).split(os.sep)
             path = string.join(tmp[:len(tmp) - 3], os.sep)
             if is_verbose():
                 print "Checking jni.h..."
-            if not os.path.exists('%s/include/jni.h' % path):
-                raise MKException("Failed to detect jni.h at '%s/include'" % path)
+            if not os.path.exists('%s%sinclude%sjni.h' % (path, os.sep, os.sep)):
+                raise MKException("Failed to detect jni.h at '%s%sinclude'" % (path, os.sep))
             JAVA_HOME = path
             return
     raise MKException('Failed to find JAVA_HOME')
@@ -344,6 +350,7 @@ def display_help(exit_code):
         print "  CXXFLAGS   C++ compiler flags"
     print "  JAVA       Java virtual machine (only relevant if -j or --java option is provided)"
     print "  JAVAC      Java compiler (only relevant if -j or --java option is provided)"
+    print "  JAVA_HOME  JDK installation directory (only relevant if -j or --java option is provided)"
     exit(exit_code)
 
 # Parse configuration option for mk_make script
@@ -716,9 +723,9 @@ class ExeComponent(Component):
     def require_mem_initializer(self):
         return True
 
-    # All executables are included in the all: rule
+    # All executables (to be installed) are included in the all: rule
     def main_component(self):
-        return True
+        return self.install
 
     def mk_install(self, out):
         if self.install:
@@ -924,13 +931,16 @@ class JavaDLLComponent(Component):
         if is_java_enabled():
             dllfile = '%s$(SO_EXT)' % self.dll_name
             out.write('%s: %s/Z3Native.java %s$(SO_EXT)\n' % (dllfile, self.to_src_dir, get_component('api_dll').dll_name))
-            out.write('\tcd %s; %s Z3Native.java\n' % (self.to_src_dir, JAVAC))
             if IS_WINDOWS:
+                out.write('\tcd %s && %s Z3Native.java\n' % (unix_path2dos(self.to_src_dir), JAVAC))
                 out.write('\tmove %s\\*.class .\n' % unix_path2dos(self.to_src_dir))
+                out.write('\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)Z3Native$(OBJ_EXT) -I"%s/include" -I"%s/include/win32" -I%s %s/Z3Native.c\n' % (JAVA_HOME, JAVA_HOME, get_component('api').to_src_dir, self.to_src_dir))
+                out.write('\t$(SLINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS) Z3Native$(OBJ_EXT) libz3.lib\n' % dllfile)
             else:
+                out.write('\tcd %s; %s Z3Native.java\n' % (self.to_src_dir, JAVAC))
                 out.write('\tmv %s/*.class .\n' % self.to_src_dir)
-            out.write('\t$(CC) $(CXXFLAGS) $(CXX_OUT_FLAG)Z3Native$(OBJ_EXT) -I"%s/include" -I%s %s/Z3Native.c\n' % (JAVA_HOME, get_component('api').to_src_dir, self.to_src_dir))
-            out.write('\t$(CC) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS) -L. Z3Native$(OBJ_EXT) -lz3\n' % dllfile)
+                out.write('\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)Z3Native$(OBJ_EXT) -I"%s/include" -I%s %s/Z3Native.c\n' % (JAVA_HOME, get_component('api').to_src_dir, self.to_src_dir))
+                out.write('\t$(SLINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS) -L. Z3Native$(OBJ_EXT) -lz3\n' % dllfile)
             out.write('%s: %s\n\n' % (self.name, dllfile))
     
     def main_component(self):
@@ -1146,6 +1156,12 @@ def mk_config():
                     'SLINK_EXTRA_FLAGS=/link /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n')
 
         # End of Windows VS config.mk
+        if is_verbose():
+            print '64-bit:         %s' % is64()
+            if is_java_enabled():
+                print 'Java Home:      %s' % JAVA_HOME
+                print 'Java Compiler:  %s' % JAVAC
+                print 'Java VM:        %s' % JAVA
     else:
         global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS
         ARITH = "internal"
diff --git a/scripts/update_api.py b/scripts/update_api.py
index 568737eee..a82ac205a 100644
--- a/scripts/update_api.py
+++ b/scripts/update_api.py
@@ -504,7 +504,10 @@ def mk_java():
     java_native.write('  public static class IntPtr { public int value; }\n')
     java_native.write('  public static class LongPtr { public long value; }\n')
     java_native.write('  public static class StringPtr { public String value; }\n')
-    java_native.write('  static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name[3:]) # We need 3: to extract the prexi 'lib' form the dll_name
+    if is_windows():
+        java_native.write('  static { System.loadLibrary("%s"); }\n' % get_component('java'))
+    else:
+        java_native.write('  static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name[3:]) # We need 3: to extract the prexi 'lib' form the dll_name
     for name, result, params in _dotnet_decls:
         java_native.write('  public static native %s %s(' % (type2java(result), java_method_name(name)))
         first = True
@@ -543,7 +546,7 @@ def mk_java():
             if k == OUT or k == INOUT:
                 java_wrapper.write('  %s _a%s;\n' % (type2str(param_type(param)), i))
             elif k == IN_ARRAY or k == INOUT_ARRAY:
-                java_wrapper.write('  %s * _a%s = (%s *) (*jenv)->%s(jenv, a%s, NULL);\n' % (type2str(param_type(param)),
+                java_wrapper.write('  %s * _a%s = (%s *) jenv->%s(a%s, NULL);\n' % (type2str(param_type(param)),
                                                                                              i,
                                                                                              type2str(param_type(param)),
                                                                                              java_get_array_elements(param),
@@ -555,7 +558,7 @@ def mk_java():
                                                                                                      param_array_capacity_pos(param), 
                                                                                                      type2str(param_type(param))))
             elif k == IN and param_type(param) == STRING:
-                java_wrapper.write('  Z3_string _a%s = (Z3_string) (*jenv)->GetStringUTFChars(jenv, a%s, NULL);\n' % (i, i))
+                java_wrapper.write('  Z3_string _a%s = (Z3_string) jenv->GetStringUTFChars(a%s, NULL);\n' % (i, i))
             i = i + 1
         # invoke procedure
         java_wrapper.write('  ')
@@ -585,34 +588,34 @@ def mk_java():
         for param in params:
             k = param_kind(param)
             if k == OUT_ARRAY:
-                java_wrapper.write('  (*jenv)->%s(jenv, a%s, 0, (jsize)a%s, (%s *) _a%s);\n' % (java_set_array_region(param),
+                java_wrapper.write('  jenv->%s(a%s, 0, (jsize)a%s, (%s *) _a%s);\n' % (java_set_array_region(param),
                                                                                                 i,
                                                                                                 param_array_capacity_pos(param),
                                                                                                 java_array_element_type(param),
                                                                                                 i))
                 java_wrapper.write('  free(_a%s);\n' % i)
             elif k == IN_ARRAY or k == OUT_ARRAY:
-                java_wrapper.write('  (*jenv)->%s(jenv, a%s, (%s *) _a%s, JNI_ABORT);\n' % (java_release_array_elements(param), 
+                java_wrapper.write('  jenv->%s(a%s, (%s *) _a%s, JNI_ABORT);\n' % (java_release_array_elements(param), 
                                                                                             i, 
                                                                                             java_array_element_type(param),
                                                                                             i))
             elif k == OUT or k == INOUT:
                 if param_type(param) == INT or param_type(param) == UINT:
                     java_wrapper.write('  {\n')
-                    java_wrapper.write('     jclass mc    = (*jenv)->GetObjectClass(jenv, a%s);\n' % i)
-                    java_wrapper.write('     jfieldID fid = (*jenv)->GetFieldID(jenv, mc, "value", "I");\n')
-                    java_wrapper.write('     (*jenv)->SetIntField(jenv, a%s, fid, (jint) _a%s);\n' % (i, i))
+                    java_wrapper.write('     jclass mc    = jenv->GetObjectClass(a%s);\n' % i)
+                    java_wrapper.write('     jfieldID fid = jenv->GetFieldID(mc, "value", "I");\n')
+                    java_wrapper.write('     jenv->SetIntField(a%s, fid, (jint) _a%s);\n' % (i, i))
                     java_wrapper.write('  }\n')
                 else:
                     java_wrapper.write('  {\n')
-                    java_wrapper.write('     jclass mc    = (*jenv)->GetObjectClass(jenv, a%s);\n' % i)
-                    java_wrapper.write('     jfieldID fid = (*jenv)->GetFieldID(jenv, mc, "value", "J");\n')
-                    java_wrapper.write('     (*jenv)->SetLongField(jenv, a%s, fid, (jlong) _a%s);\n' % (i, i))
+                    java_wrapper.write('     jclass mc    = jenv->GetObjectClass(a%s);\n' % i)
+                    java_wrapper.write('     jfieldID fid = jenv->GetFieldID(mc, "value", "J");\n')
+                    java_wrapper.write('     jenv->SetLongField(a%s, fid, (jlong) _a%s);\n' % (i, i))
                     java_wrapper.write('  }\n')
             i = i + 1
         # return
         if result == STRING:
-            java_wrapper.write('  return (*jenv)->NewStringUTF(jenv, result);\n')
+            java_wrapper.write('  return jenv->NewStringUTF(result);\n')
         elif result != VOID:
             java_wrapper.write('  return (%s) result;\n' % type2javaw(result))
         java_wrapper.write('}\n')