Compiling PythonService.cpp from win32 python extension build - c++

I need some help with building pythonservice.exe (PythonService.cpp) from https://github.com/kovidgoyal/pywin32/tree/master/win32/src.
It is said (https://github.com/kovidgoyal/pywin32) that VS2015 is required, so I downloaded the community version. I opened a new win32 console project and imported to source files only the PythonService.cpp.
In project settings I have:
Include directories: C:\Python27_32bit\include;C:\pywin32-master\win32\src;$(VC_IncludePath);$(WindowsSDK_IncludePath);
Library directories: C:\Python27\Lib\site-packages\win32\libs;C:\Python27_32bit\libs;C:\pywin32-master\win32\src;$(LibraryPath);C:\pywin32-master\lib\x32\win32\libs;
Now, when building I get the unresolved external symbol error:
1>PythonService.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl GetPythonTraceback(struct _object *,struct _object *,struct _object *)" (__imp_?GetPythonTraceback##YAPADPAU_object##00#Z)
GetPythonTraceback is defined in PyWinTypesmodule.cpp but when I add it to my project it gives other errors requiring other unresolved functions. I don't want to compile the whole win32 project. All I need is pythonservices.exe. Is this possible to accomplish without compiling the whole project?
Would be thankful for any help!
Thanks!
Alexei

I eventually found a solution to my question (not without help from #CristiFati 's answer!). I downloaded https://github.com/mhammond/pywin32/tree/b222 and used the setup.py script to build the whole package. Also, I installed the required SDK package (for python 2.6+ Microsoft Windows SDK for Windows 7 and .NET Framework 4 (version 7.1)). After digging a little bit I found that the compilation and linkage are done with Visual C++ for Python 9.0 and below are the compilation and linkage recipes for pythonservice.pyd and pythonservice.exe (python lists for cmd parameter to calls of os.spawnv(os.P_WAIT, executable, cmd):
For pythonservice.pyd:
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\WinSDK\\Bin\\x64\\mc.exe"','-h', 'win32/src', '-r', 'build\\temp.win-amd64-2.7\\Release\\win32/src', 'win32/src/PythonServiceMessages.mc']
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\WinSDK\\Bin\\x64\\rc.exe"','/fobuild\\temp.win-amd64-2.7\\Release\\win32/src/PythonServiceMessages.res','build\\temp.win-amd64-2.7\\Release\\win32/src\\PythonServiceMessages.rc']
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\VC\\Bin\\amd64\\cl.exe"','/c', '/nologo', '/Ox','/W3','/GS', '/DNDEBUG', '/MD', '-DDISTUTILS_BUILD', '-D_CRT_SECURE_NO_WARNINGS','-Icom/win32com/src/include', '-Iwin32/src', '-IC:\\Python27\\include', '-IC:\\Python27\\PC','"-IC:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\include"', '/Tpwin32/src/PythonService.cpp','/Fobuild\\temp.win-amd64-2.7\\Release\\win32/src/PythonService.obj', '-DPYSERVICE_BUILD_DLL', '/Zi','/Fdbuild\\temp.win-amd64-2.7\\Release\\servicemanager_vc.pdb', '/EHsc', '/DUNICODE', '/D_UNICODE', '/DWINNT']
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\VC\\Bin\\amd64\\link.exe"', '/DLL', '/nologo', '/INCREMENTAL:NO', '/LIBPATH:C:\\Python27\\libs', '/LIBPATH:C:\\Python27\\PCbuild\\amd64', '/LIBPATH:C:\\Python27\\PC\\VS9.0\\amd64', '/LIBPATH:build\\temp.win-amd64-2.7\\Release', '"/LIBPATH:C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\lib\\x64"', 'user32.lib', 'ole32.lib', 'advapi32.lib', 'shell32.lib', '/EXPORT:initservicemanager', 'build\\temp.win-amd64-2.7\\Release\\win32/src/PythonServiceMessages.res', 'build\\temp.win-amd64-2.7\\Release\\win32/src/PythonService.obj', '/OUT:build\\lib.win-amd64-2.7\\win32\\servicemanager.pyd', '/IMPLIB:build\\temp.win-amd64-2.7\\Release\\win32/src\\servicemanager.lib', '/MANIFEST:NO', '/MACHINE:amd64', '/BASE:0x1e7d0000', '/DEBUG', '/PDB:build\\temp.win-amd64-2.7\\Release\\servicemanager.pdb']
For pythonservice.exe:
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\VC\\Bin\\amd64\\cl.exe"','/c', '/nologo', '/Ox', '/W3', '/GS-', '/DNDEBUG', '/MD', '-DDISTUTILS_BUILD', '-D_CRT_SECURE_NO_WARNINGS','-Icom/win32com/src/include', '-Iwin32/src', '-IC:\\Python27\\include', '-IC:\\Python27\\PC','"-IC:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\include"','/Tpwin32\\src\\PythonService.cpp', '/Fobuild\\temp.win-amd64-2.7\\Release\\pythonservice\\win32\\src\\PythonService.obj','/Zi', '/Fdbuild\\temp.win-amd64-2.7\\Release\\pythonservice_vc.pdb', '/EHsc', '/DUNICODE', '/D_UNICODE', '/DWINNT']
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\WinSDK\\Bin\\x64\\rc.exe"','-DDISTUTILS_BUILD', '-D_CRT_SECURE_NO_WARNINGS', '-Icom/win32com/src/include', '-Iwin32/src', '-IC:\\Python27\\include','-IC:\\Python27\\PC', '"-IC:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\include"','/fobuild\\temp.win-amd64-2.7\\Release\\pythonservice\\win32\\src\\PythonService.res', 'win32\\src\\PythonService.rc']
['"C:\\Users\\username\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\VC\\Bin\\amd64\\link.exe"','/nologo', '/INCREMENTAL:NO', '/LIBPATH:C:\\Python27\\libs', '/LIBPATH:C:\\Python27\\PCbuild\\amd64','/LIBPATH:C:\\Python27\\PC\\VS9.0\\amd64', '/LIBPATH:build\\temp.win-amd64-2.7\\Release','"/LIBPATH:C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\lib\\x64"', 'user32.lib', 'advapi32.lib', 'ole32.lib','shell32.lib', 'build\\temp.win-amd64-2.7\\Release\\pythonservice\\win32\\src\\PythonService.obj','build\\temp.win-amd64-2.7\\Release\\pythonservice\\win32\\src\\PythonService.res','/OUT:build\\lib.win-amd64-2.7\\win32\\pythonservice.exe','/MANIFESTFILE:build\\temp.win-amd64-2.7\\Release\\pythonservice\\win32\\src\\pythonservice.exe.manifest','/SUBSYSTEM:CONSOLE', '/MACHINE:amd64', '/DEBUG', '/PDB:build\\temp.win-amd64-2.7\\Release\\pythonservice.pdb']
['mt.exe', '-nologo', '-manifest','build\\temp.win-amd64-2.7\\Release\\pythonservice\\win32\\src\\pythonservice.exe.manifest.orig','-outputresource:build\\lib.win-amd64-2.7\\win32\\pythonservice.exe;1']
['C:\\Python27\\python.exe', 'C:\\Python27\\Lib\\site-packages\\win32\\lib\\win32verstamp.py', '--version=2.7.222.1', '--comments=https://github.com/mhammond/pywin32', '--original-filename=pythonservice.exe','--product=PyWin32', '--quiet', 'build\\lib.win-amd64-2.7\\win32\\pythonservice.exe']

The 1st thing that I see here is combining Python 2.7 and VStudio 2015. According to [Python.Wiki]: WindowsCompilers, for VStudion 2015 (or 14.0), you should use Python 3.5 or Python 3.6 (I have the former). Check [SO]: Simstring (python) installation in windows (#CristiFati's answer) - (the 1st part), about compatibilities and restrictions in terms of:
Platform
Tools versions
Architecture
Also, some (basic) information about building C code on Win: [SO]: LNK2005 Error in CLR Windows Form (#CristiFati's answer)
Downloaded original PyWin32 from: [GitHub]: mhammond/pywin32 - (b222) (to pywin32-b222.zip). The "${PYWIN32_SRC_DIR}\win32\src\PythonService.cpp" is identical to its correspondent from the URL you mentioned
Create a dir and cd to it (should be empty). This will be the %ROOT_DIR%, and all the paths that I'm going to use will be relative to it (except of course for absolute ones), and this will be the default dir (when unspecified)
Unpack the downloaded .zip somewhere. We (directly) need from it:
"${PYWIN32_SRC_DIR}\win32\src\PythonService.cpp" - as you mentioned it, the source code
"${PYWIN32_SRC_DIR}\win32\src\PythonServiceMessages.mc" - message text file
I copied them in a dir called src:
E:\Work\Dev\StackOverflow\q048931801>dir /b
src
E:\Work\Dev\StackOverflow\q048931801>dir /b "src"
PythonService.cpp
PythonServiceMessages.mc
Since the build involves resources and Custom Build Steps (that I'm not very good at), and also it doesn't involve lots of files, I'm not going to use the IDE
The next best approach would be to use Makefiles, but since VStudio has a bug [MS.MSDN]: ntwin32.mak not found:
It affects VStudio 2015 as well
It requires a one time fix
I did the fix on my machine and it works, but you most likely didn't on yours, and I don't want to unnecessarily burden you with this step
I am going to do everything manually
Setting up VStudio environment:
[MS.Docs]: Use the MSVC toolset from the command line
You could also check [SO]: How to build a DLL version of libjpeg 9b? (#CristiFati's answer) (1st part) for more details about manually building
I'm using vcvarsall.bat ([MS.MSDN]: Where is vcvarsall.bat file?) from the command line
E:\Work\Dev\StackOverflow\q048931801>"c:\Install\x86\Microsoft\Visual Studio Community\2015\VC\vcvarsall.bat" amd64
You'll (obviously) need to supply your VStudio installation path
amd64 is because I only have the 64bit (x64 or AMD64) version of Python 3.5 installed
Compile the message file into a resource file - using mc.exe ([MS.Docs]: Message Compiler (MC.exe))
E:\Work\Dev\StackOverflow\q048931801>mc "src\PythonServiceMessages.mc"
MC: Compiling src\PythonServiceMessages.mc
E:\Work\Dev\StackOverflow\q048931801>dir /b
MSG00001.bin
PythonServiceMessages.h
PythonServiceMessages.rc
src
As seen, it generated 3 new files
Compile the resource file - using rc.exe ([MS.Docs]: Resource Compiler)
E:\Work\Dev\StackOverflow\q048931801>rc /NOLOGO /r "PythonServiceMessages.rc"
E:\Work\Dev\StackOverflow\q048931801>dir /b
MSG00001.bin
PythonServiceMessages.h
PythonServiceMessages.rc
PythonServiceMessages.res
src
Done with the resources, going to the code (PythonService.cpp). As you already mentioned, it won't compile OOTB. I'm not going to list all the failed attempts, instead I'm going to list the steps I took, and only show the final (successful) compile. So, the file needs code from other files. The simplest way is to copy it from the other files and place it in this one. I chose to paste it around line #1530 (just before the "* Entry points" section):
PyWinTypesModule.cpp - GetPythonTraceback function (lines 1142 - 1235)
PyUnicode.cpp - PyWin_AutoFreeBstr::PyWin_AutoFreeBstr, PyWin_AutoFreeBstr::~PyWin_AutoFreeBstr, PyWin_AutoFreeBstr::SetBstr (lines 204 - 219)
Either apply the changes manually, or save:
--- PythonService.cpp.orig 2018-01-20 21:43:10.000000000 +0200
+++ PythonService.cpp 2018-02-23 20:02:20.186966800 +0200
## -1529,6 +1529,120 ##
}
}
+// #TODO: cfati - copied from PyUnicode.cpp (lines 204 - 219)
+PyWin_AutoFreeBstr::PyWin_AutoFreeBstr( BSTR bstr /*= NULL*/ )
+ : m_bstr(bstr)
+{
+ return;
+}
+
+PyWin_AutoFreeBstr::~PyWin_AutoFreeBstr()
+{
+ SysFreeString(m_bstr);
+}
+
+void PyWin_AutoFreeBstr::SetBstr( BSTR bstr )
+{
+ SysFreeString(m_bstr);
+ m_bstr = bstr;
+}
+
+
+// #TODO: cfati - copied from PyWinTypesModule.cpp (lines 1142 - 1235)
+// Function to format a python traceback into a character string.
+#define GPEM_ERROR(what) {errorMsg = "<Error getting traceback - " ## what ## ">";goto done;}
+char *GetPythonTraceback(PyObject *exc_type, PyObject *exc_value, PyObject *exc_tb)
+{
+ // Sleep (30000); // Time enough to attach the debugger (barely)
+ char *result = NULL;
+ char *errorMsg = NULL;
+ PyObject *modStringIO = NULL;
+ PyObject *modTB = NULL;
+ PyObject *obFuncStringIO = NULL;
+ PyObject *obStringIO = NULL;
+ PyObject *obFuncTB = NULL;
+ PyObject *argsTB = NULL;
+ PyObject *obResult = NULL;
+
+ /* Import the modules we need - cStringIO and traceback */
+#if (PY_VERSION_HEX < 0x03000000)
+ modStringIO = PyImport_ImportModule("cStringIO");
+#else
+ // In py3k, cStringIO is in "io"
+ modStringIO = PyImport_ImportModule("io");
+#endif
+
+ if (modStringIO==NULL) GPEM_ERROR("cant import cStringIO");
+ modTB = PyImport_ImportModule("traceback");
+ if (modTB==NULL) GPEM_ERROR("cant import traceback");
+
+ /* Construct a cStringIO object */
+ obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
+ if (obFuncStringIO==NULL) GPEM_ERROR("cant find cStringIO.StringIO");
+ obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
+ if (obStringIO==NULL) GPEM_ERROR("cStringIO.StringIO() failed");
+
+ /* Get the traceback.print_exception function, and call it. */
+ obFuncTB = PyObject_GetAttrString(modTB, "print_exception");
+ if (obFuncTB==NULL) GPEM_ERROR("cant find traceback.print_exception");
+ argsTB = Py_BuildValue("OOOOO"
+#if (PY_VERSION_HEX >= 0x03000000)
+ "i"
+ // Py3k has added an undocumented 'chain' argument which defaults to True
+ // and causes all kinds of exceptions while trying to print a goddam exception
+#endif
+ ,
+ exc_type ? exc_type : Py_None,
+ exc_value ? exc_value : Py_None,
+ exc_tb ? exc_tb : Py_None,
+ Py_None, // limit
+ obStringIO
+#if (PY_VERSION_HEX >= 0x03000000)
+ ,0 // Goddam undocumented 'chain' param, which defaults to True
+#endif
+ );
+ if (argsTB==NULL) GPEM_ERROR("cant make print_exception arguments");
+
+ obResult = PyObject_CallObject(obFuncTB, argsTB);
+ if (obResult==NULL){
+ // Chain parameter when True causes traceback.print_exception to fail, leaving no
+ // way to see what the original problem is, or even what error print_exc raises
+ // PyObject *t, *v, *tb;
+ // PyErr_Fetch(&t, &v, &tb);
+ // PyUnicodeObject *uo=(PyUnicodeObject *)v;
+ // DebugBreak();
+ GPEM_ERROR("traceback.print_exception() failed");
+ }
+ /* Now call the getvalue() method in the StringIO instance */
+ Py_DECREF(obFuncStringIO);
+ obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
+ if (obFuncStringIO==NULL) GPEM_ERROR("cant find getvalue function");
+ Py_DECREF(obResult);
+ obResult = PyObject_CallObject(obFuncStringIO, NULL);
+ if (obResult==NULL) GPEM_ERROR("getvalue() failed.");
+
+ /* And it should be a string all ready to go - duplicate it. */
+ if (PyString_Check(obResult))
+ result = strdup(PyString_AsString(obResult));
+#if (PY_VERSION_HEX >= 0x03000000)
+ else if (PyUnicode_Check(obResult))
+ result = strdup(_PyUnicode_AsString(obResult));
+#endif
+ else
+ GPEM_ERROR("getvalue() did not return a string");
+
+done:
+ if (result==NULL && errorMsg != NULL)
+ result = strdup(errorMsg);
+ Py_XDECREF(modStringIO);
+ Py_XDECREF(modTB);
+ Py_XDECREF(obFuncStringIO);
+ Py_XDECREF(obStringIO);
+ Py_XDECREF(obFuncTB);
+ Py_XDECREF(argsTB);
+ Py_XDECREF(obResult);
+ return result;
+}
/*************************************************************************
*
as "src\PythonService.diff" That is a diff. See [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (#CristiFati's answer) (Patching utrunner section) for how to apply patches on Win (basically, every line that starts with one "+" sign goes in, and every line that starts with one "-" sign goes out). I am using Cygwin:
E:\Work\Dev\StackOverflow\q048931801>"c:\Install\x64\Cygwin\Cygwin\AllVers\bin\patch.exe" "src\PythonService.cpp" "src\PythonService.diff"
patching file 'src\PythonService.cpp'
Compile the code - using cl.exe ([MS.Docs]: Compiling a C/C++ Program)
E:\Work\Dev\StackOverflow\q048931801>cl /GS /W1 /Zc:wchar_t /I"." /I"c:\Install\x64\Python\Python\3.5\include" /I"e:\Work\Dev\Fati\WinBuild\OPSWpython27\src\pywin32-b222\win32\src" /Gm- /O2 /Zc:inline /fp:precise /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "WIN64" /D "NDEBUG" /D "BUILD_PYWINTYPES" /errorReport:prompt /WX- /Zc:forScope /Gd /MD /c "src\PythonService.cpp"
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
PythonService.cpp
E:\Work\Dev\StackOverflow\q048931801>dir /b "*.obj"
PythonService.obj
Arguments are standard ones ([MS.Docs]: Compiler Options Listed Alphabetically), specific for this case:
/D "BUILD_PYWINTYPES" (macro definition)
/D "WIN64" - 64bit
Include paths (/I) - you'll have to adapt them to match your paths:
"c:\Install\x64\Python\Python\3.5\include" (Python path)
"e:\Work\Dev\Fati\WinBuild\OPSWpython27\src\pywin32-b222\win32\src" (pywin32 path)
Link everything together - using link.exe ([MS.Docs]: Linking)
E:\Work\Dev\StackOverflow\q048931801>link /NOLOGO /MACHINE:X64 /SUBSYSTEM:CONSOLE /ERRORREPORT:PROMPT /LIBPATH:"c:\Install\x64\Python\Python\3.5\libs" /NXCOMPAT /DYNAMICBASE "kernel32.lib" "user32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "PythonService.obj" "PythonServiceMessages.res" /OUT:"PythonService.exe"
Creating library PythonService.lib and object PythonService.exp
E:\Work\Dev\StackOverflow\q048931801>dir /b "*.exe"
PythonService.exe
Again, arguments are standard ones ([MS.Docs]: Linker Options), specific for this case:
/MACHINE:X64 - 64bit
Library paths (/LIBPATH) - adapt it to match yours:
"c:\Install\x64\Python\Python\3.5\libs (Python path)
One last step before running the service. Since it depends on python35.dll, the OS must know where to look for it. Simplest way is to add its path to %PATH% ([MS.Docs]: Dynamic-Link Library Search Order)
E:\Work\Dev\StackOverflow\q048931801>set PATH=%PATH%;c:\Install\x64\Python\Python\3.5
E:\Work\Dev\StackOverflow\q048931801>PythonService.exe
P - Python Service Manager
Options:
-register - register the EXE - this should generally not be necessary.
-debug servicename [parms] - debug the Python service.
NOTE: You do not start the service using this program - start the
service using Control Panel, or 'net start service_name'

Related

How to fix Cannot find libcocos2dcpp.so when trying to support 64 bit

i have a cocos2dx game in android studio and when I'm trying to make it support 64 bit requirement then i got this error "couldn't find "libcocos2dcpp.so" " when i start project on my phone
what I've done to support the 64 bit :
i searched on cocos2dx forum for solution to support 64 bit and i found a solution :
* i've Modified Application.MK file : adding APP_ABI := armeabi armeabi-v7a arm64-v8a
* gradle.properties : adding PROP_APP_ABI=armeabi-v7a:arm64-v8a
* build.gradle : adding ndk.abiFilters 'armeabi-v7a', 'arm64-v8a'
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.xxxxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/base.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_dependencies_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_resources_apk.apk", zip file "/data/app/com.xxxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_0_apk.apk", zip file "/data/app/com.xxxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_1_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_2_apk.apk", zip file "/data/app/com.xxxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_3_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_4_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_5_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_6_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_7_apk.apk", zip file "/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_8_apk.apk", zip file "/data/app/com.xxxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.xxx.kidslearngame-oq27wbETBHeT2MFhWg9cOw==/lib/arm64, /system/lib64, /system/vendor/lib64]]] couldn't find "libcocos2dcpp.so"
those are the solution that i found but when i run the app on my phone it's crashing and giving me the error above but when i removed ndk.abiFilters 'armeabi-v7a', 'arm64-v8a' from gradle.build it's working good but when i upload it to play store they show me the warning message "your app those not support the 64 bit requirement "
Perhaps you should clear the project and in Android Studio select in the menu File -> Invalidate Caches / Restart
Below I give the settings that work for me:
In Application.mk
APP_ABI := arm64-v8a
In gradle.properties
PROP_APP_ABI=armeabi-v7a:arm64-v8a
In app/build.gradle
android {
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
buildToolsVersion PROP_BUILD_TOOLS_VERSION
def versionMajor = 0
def versionMinor = 9
def versionPatch = 0
def versionBuild = 0
defaultConfig {
applicationId "YOUR APP ID"
minSdkVersion PROP_MIN_SDK_VERSION
targetSdkVersion PROP_TARGET_SDK_VERSION
// versionCode 1
// versionName "1.0"
versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
externalNativeBuild {
if (PROP_BUILD_TYPE == 'ndk-build') {
ndkBuild {
targets 'MyGame'
arguments 'NDK_TOOLCHAIN_VERSION=clang'
arguments '-j' + Runtime.runtime.availableProcessors()
}
}
else if (PROP_BUILD_TYPE == 'cmake') {
cmake {
targets 'MyGame'
arguments "-DCMAKE_FIND_ROOT_PATH=", "-DANDROID_STL=c++_static", "-DANDROID_TOOLCHAIN=clang", "-DANDROID_ARM_NEON=TRUE", \
"-DUSE_CHIPMUNK=TRUE", "-DUSE_BULLET=TRUE"
cppFlags "-frtti -fexceptions"
// prebuilt root must be defined as a directory which you have right to access or create if you use prebuilt
// set "-DGEN_COCOS_PREBUILT=ON" and "-DUSE_COCOS_PREBUILT=OFF" to generate prebuilt, this way build cocos2d-x libs
// set "-DGEN_COCOS_PREBUILT=OFF" and "-DUSE_COCOS_PREBUILT=ON" to use prebuilt, this way not build cocos2d-x libs
//arguments "-DCOCOS_PREBUILT_ROOT=/Users/laptop/cocos-prebuilt"
//arguments "-DGEN_COCOS_PREBUILT=OFF", "-DUSE_COCOS_PREBUILT=OFF"
}
}
}
ndk {
abiFilters = []
abiFilters.addAll(PROP_APP_ABI.split(':').collect{it as String})
}
}
splits {
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
enable true
//enable gradle.startParameter.taskNames.contains(":app:assembleRelease")
//enable project.hasProperty('splitApks')
// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86, armeabi-v7a, and mips.
reset()
// Specifies a list of ABIs that Gradle should create APKs for.
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
// Specifies that we want to also generate a universal APK that includes all ABIs.
universalApk true
}
}
// Map for the version code that gives each ABI a value.
def abiCodes = ['x86':3, 'x86_64':4, 'armeabi-v7a':1, 'arm64-v8a':2]
// APKs for the same app that all have the same version information.
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK.
variant.outputs.each {
output ->
def abiName = output.getFilter(OutputFile.ABI)
output.versionCodeOverride = abiCodes.get(abiName, 0) * 1000000 + android.defaultConfig.versionCode
}
}
. . . . . . . . .
. . . . . . . . .
I hope this helps.

Using boost with Bazel under Windows 10 and Visual Studio Community 2019

I have set up a simple C++ program that makes use of the boost filesystem module. To build the program I use Bazel 0.25.0. I am working under Windows 10 x64.
I installed Visual Studio 2019 Community Edtion and set BAZEL_VC to E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC. I have installed the MSYS2 shell.
Here are my files (can be found also on GitHub):
WORKSPACE
workspace(name = "BoostFilesystemDemo")
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Boost
git_repository(
name = "com_github_nelhage_rules_boost",
commit = "6681419da0163d097d4e09c0756c0d8b785d2aa8",
remote = "https://github.com/nelhage/rules_boost",
shallow_since = "1556401984 -0700"
)
load("#com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps")
boost_deps()
main.cpp
#include <iostream>
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cout << "Usage: tut1 path\n";
return 1;
}
std::cout << argv[1] << " " << file_size(argv[1]) << '\n';
return 0;
}
BUILD
cc_binary(
name = "FilesystemTest",
srcs = ["main.cpp"],
deps = [
"#boost//:filesystem",
],
)
When I try to build I receive the following error message (unfortunately mixed with some German language - Datei kann nicht gefunden werden means file not found)
PS E:\dev\BazelDemos\BoostFilesystemDemo> bazel build //...
INFO: Analyzed target //:FilesystemTest (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
ERROR: E:/dev/bazeldemos/boostfilesystemdemo/BUILD:1:1: Linking of rule '//:FilesystemTest' failed (Exit 1104)
LINK : fatal error LNK1104: Datei "libboost_filesystem-vc141-mt-x64-1_68.lib" kann nicht geĆ·ffnet werden.
Target //:FilesystemTest failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 1.175s, Critical Path: 0.12s
INFO: 0 processes.
FAILED: Build did NOT complete successfully
Does anyone have some idea to fix this problem (compiling the source using Bazel 0.25.0 or up, Visual Studio 2019 Community Edition, Windows 10 x64, Target should be x64)? Using Ubuntu 18.04 everything went fine.
Switching to another git repository that provides boost is also fine for me.
I want to also to use other parts of the boost library such as boost signals2, boost log, boost algorithm and boost compute.
Modify the BUILD.boost this way:
diff --git a/BUILD.boost b/BUILD.boost
index a3a2195..2cffdda 100644
--- a/BUILD.boost
+++ b/BUILD.boost
## -623,6 +623,14 ## boost_library(
":system",
":type_traits",
],
+ defines = select({
+ ":linux_arm": [],
+ ":linux_x86_64": [],
+ ":osx_x86_64": [],
+ ":windows_x86_64": [
+ "BOOST_ALL_NO_LIB",
+ ],
+ }),
)
boost_library(
## -1491,6 +1499,14 ## boost_library(
":predef",
":utility",
],
+ defines = select({
+ ":linux_arm": [],
+ ":linux_x86_64": [],
+ ":osx_x86_64": [],
+ ":windows_x86_64": [q
+ "BOOST_ALL_NO_LIB",
I cloned the rules_boost repo and applied the above changes - the cloned repository can be used direclty in the WORKSPACE file:
workspace(name = "BoostFilesystemDemo")
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Boost
git_repository(
name = "com_github_nelhage_rules_boost",
commit = "f0d2a15d6dd5f0667cdaa6da9565ccf87b84c468",
remote = "https://github.com/Vertexwahn/rules_boost",
shallow_since = "1557766870 +0200"
)
load("#com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps")
boost_deps()
Currently, a pull request is running to merge these changes into the original repository: https://github.com/nelhage/rules_boost/pull/123

How to build OpenSSL on Windows with Visual Studio 2017?

I am trying to use OpenSSL but I am stuck on the step of compiling. The OpenSSL project has very unfriendly (bad) documentation.
Is there any actual help how to build the latest OpenSSL version on Windows with Visual Studio 2017?
I didn't find any helpful information on the official OpenSSL site. Yes, there are a lot of posts on the Internet about OpenSSL compilation, but all of them are obsolete.
I've not used VS2017 but previous versions. I imagine it is much the same. Note the instructions below are for OpenSSL 1.1.0 or above. They do not work for OpenSSL 1.0.2. In brief the steps are:
Install Perl (either ActiveState or Strawberry)
[EDIT, see my (kritzel_sw) comment below: I would strongly recommend to use Strawberry)]
Install NASM
Make sure both Perl and NASM are on your %PATH%
Fire up a Visual Studio Developer Command Prompt with administrative privileges (make sure you use the 32-bit one if you are building 32-bit OpenSSL, or the 64-bit one if you are building 64-bit OpenSSL)
From the root of the OpenSSL source directory enter perl Configure VC-WIN32, if you want 32-bit OpenSSL or perl Configure VC-WIN64A if you want 64-bit OpenSSL
Enter nmake
Enter nmake test
Enter nmake install
[EDIT, unless you change the target directory in the configuration, nmake install needs administrator privileges. So the VC command prompt must be started as administrator for this final step]
If anything goes wrong at any stage, check the INSTALL file and the NOTES.WIN file.
Modified version of The Quantum Physicist python script
It can compile OpenSSL 1.0.x or OpenSSL 1.1.x
It can compile with multiple version of Visual Studio 2017/2019 included.
1) Create the file: CompileOpenSSL.py
import os
import os.path
from subprocess import call
import shutil
import sys
import re
import argparse
# args
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--filename", help="First argument must be the tar.gz file of OpenSSL source", required=True)
parser.add_argument("-a", "--arch", help="Second argument must be x86 or amd64", required=True)
parser.add_argument("-v", "--vs_version", help="Visual Studio version (eg:90, 140, 150)", required=True)
parser.set_defaults(writeVersionInfos=False)
args = parser.parse_args()
compile_flags = "-no-asm"
#compile_flags = "-no-asm -no-shared"
openssl_32_flag = "VC-WIN32"
openssl_64_flag = "VC-WIN64A"
working_dir = os.getcwd()
dirname = args.filename.replace(".tar.gz","")
src_32_suffix = "_" + "vs" + args.vs_version + "_32"
src_64_suffix = "_" + "vs" + args.vs_version + "_64"
vs_tools_env_var = "VS" + args.vs_version + "COMNTOOLS"
if args.arch != "x86" and args.arch != "amd64":
print("Second argument must be x86 or amd64")
exit(1)
if not bool(re.match("(openssl-){1}(\d)+(.)(\d)+(.)(\d)+(\w)+(.tar.gz)",args.filename)):
print("The file given doesn't seem to be an openssl source file. It must be in the form: openssl-x.y.zw.tar.gz")
exit(1)
call("7z x -y " + args.filename) #extract the .gz file
dirname_src_32 = dirname + src_32_suffix
dirname_src_64 = dirname + src_64_suffix
dirname_bin_32 = dirname + src_32_suffix + "_build"
dirname_bin_64 = dirname + src_64_suffix + "_build"
openssl_tar_file = args.filename[0:-3]
if args.arch == "x86":
#delete previous directories
shutil.rmtree(os.getcwd()+'/'+dirname, ignore_errors=True)
shutil.rmtree(os.getcwd()+'/'+dirname_src_32, ignore_errors=True)
#extract tar file for 32
call("7z x -y " + openssl_tar_file)
os.rename(dirname, dirname_src_32)
#Compile 32
os.chdir(dirname_src_32)
print("perl Configure " + openssl_32_flag + " --prefix=" + os.path.join(working_dir,dirname_bin_32) + " " + compile_flags)
call("perl Configure " + openssl_32_flag + " --prefix=" + os.path.join(working_dir,dirname_bin_32) + " " + compile_flags,shell=True)
if( os.path.exists("ms/do_ms.bat") ):
call("ms\do_ms.bat",shell=True)
print(os.getcwd())
call("nmake -f ms/ntdll.mak",shell=True)
call("nmake -f ms/ntdll.mak install",shell=True)
else:
call("nmake",shell=True)
call("nmake test",shell=True)
call("nmake install",shell=True)
print("32-bit compilation complete.")
#Go back to base dir
os.chdir(working_dir)
################
if args.arch == "amd64":
#delete previous directories
shutil.rmtree(os.getcwd()+'/'+dirname, ignore_errors=True)
shutil.rmtree(os.getcwd()+'/'+dirname_src_64, ignore_errors=True)
#extract for 64
call("7z x -y " + openssl_tar_file)
os.rename(dirname, dirname_src_64)
#Compile 64
os.chdir(dirname_src_64)
call("perl Configure " + openssl_64_flag + " --prefix=" + os.path.join(working_dir,dirname_bin_64) + " " + compile_flags,shell=True)
if( os.path.exists("ms\do_ms.bat") ):
call("ms\do_win64a.bat",shell=True)
call("nmake -f ms/ntdll.mak",shell=True)
call("nmake -f ms/ntdll.mak install",shell=True)
else:
call("nmake",shell=True)
call("nmake test",shell=True)
call("nmake install",shell=True)
print("64-bit compilation complete.")
#Go back to base dir
os.chdir(working_dir)
################
os.remove(openssl_tar_file)
2) Create the file: CompileOpenSSL_vs.cmd
ECHO --------------------------------------
ECHO Require Python, 7Zip, PERL and NASM in PATH
ECHO --------------------------------------
Rem ------------------------------------------------------
Rem TO CONFIGURE -----------------------------------------
Rem ------------------------------------------------------
Rem SET YOUR LOCAL PATHS-----------------------------------------
SET PATH=C:\Program Files (x86)\7-Zip;C:\Perl64\bin;M:\Backup\Coders\_tools\7-Zip\;%PATH%
Rem SET YOUR OPENSSL ARCHIVE-----------------------------------------
REM SET FILENAME=openssl-1.0.2r.tar.gz
SET FILENAME=openssl-1.1.1b.tar.gz
Rem SET THE VERSION OF YOUR VISUAL STUDIO-----------------------------------------
SET VSVERSION=%1
Rem ------------------------------------------------------
Rem COMPILATION LAUNCH -----------------------------------
Rem ------------------------------------------------------
Rem UTILS PATH-----
SET VSCOMNTOOLSNAME=VS%VSVERSION%COMNTOOLS
Rem Pick the good path for Visual Studio-----------------------------------------
IF %VSVERSION% GEQ 150 (
Echo DO NOT FORGET TO ADD A SYSTEM VARIABLE %VSCOMNTOOLSNAME% - like: "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\"
SET VCVARPATH="%%%VSCOMNTOOLSNAME%%%..\..\VC\Auxiliary\Build\vcvarsall.bat"
) ELSE (
SET VCVARPATH="%%%VSCOMNTOOLSNAME%%%..\..\VC\vcvarsall.bat"
)
Rem Set env -----------------------------------------
#pushd "%~dp0"
call %VCVARPATH% %2
#popd
Rem ------------------------------------------------------
Rem TEST APP EXIST -----------------------------------
Rem ------------------------------------------------------
where /q 7z.exe
IF ERRORLEVEL 1 (
ECHO The application "7z.exe" is missing. Ensure to add/install it to the PATH in beginning of this script, check SET PATH
PAUSE
EXIT /B
)
where /q perl.exe
IF ERRORLEVEL 1 (
ECHO The application "perl.exe" is missing. Ensure to add/install it to the PATH in beginning of this script, check SET PATH
PAUSE
EXIT /B
)
where /q nmake.exe
IF ERRORLEVEL 1 (
ECHO The application "nmake.exe" is missing. Ensure to add/install it to the PATH in beginning of this script, check SET PATH
PAUSE
EXIT /B
)
where /q py.exe
IF ERRORLEVEL 1 (
ECHO The application "py.exe" [shortcut of python] is missing. Ensure to add/install it to the PATH in beginning of this script, check SET PATH
PAUSE
EXIT /B
)
Rem Launch compilation -----------------------------------------
py CompileOpenSSL.py -f %FILENAME% -a %2 -v %VSVERSION%
PAUSE
3) Launch compilation from command line (Outside Visual Studio)
eg:
CompileOpenSSL_vs.cmd 150 x86
CompileOpenSSL_vs.cmd 150 amd64
CompileOpenSSL_vs.cmd 90 x86
For OpenSSL 1.0.2, I wrote a Python script that does the building for me. I have this habit of making these scripts, as I don't like to reinvent the wheel everytime I need to build something.
The script is made for OpenSSL 1.0.2. Probably the changes are minimal for OpenSSL 1.1.0.
Here's the script:
import os
from subprocess import call
import sys
import re
vs_version = "140"
compile_flags = "-no-asm -no-shared"
openssl_32_flag = "VC-WIN32"
openssl_64_flag = "VC-WIN64A"
src_32_suffix = "_" + "vs" + vs_version + "_32"
src_64_suffix = "_" + "vs" + vs_version + "_64"
vs_tools_env_var = "VS" + vs_version + "COMNTOOLS"
if len(sys.argv) < 2:
print("First argument must be the tar.gz file of OpenSSL source")
exit(1)
if len(sys.argv) < 3:
print("Second argument must be 32 or 64")
exit(1)
filename = sys.argv[1]
dirname = filename.replace(".tar.gz","")
working_dir = os.getcwd()
arch = sys.argv[2]
if arch != "32" and arch != "64":
print("Second argument must be 32 or 64")
exit(1)
if not bool(re.match("(openssl-){1}(\d)+(.)(\d)+(.)(\d)+(\w)+(.tar.gz)",filename)):
print("The file given doesn't seem to be an openssl source file. It must be in the form: openssl-x.y.zw.tar.gz")
exit(1)
call("7z x " + filename) #extract the .gz file
dirname_src_32 = dirname + src_32_suffix
dirname_src_64 = dirname + src_64_suffix
dirname_bin_32 = dirname + src_32_suffix + "_build"
dirname_bin_64 = dirname + src_64_suffix + "_build"
openssl_tar_file = filename[0:-3]
if arch == "32":
#extract tar file for 32
call("7z x " + openssl_tar_file)
os.rename(dirname, dirname_src_32)
#Compile 32
os.chdir(dirname_src_32)
call("perl Configure " + openssl_32_flag + " --prefix=" + os.path.join(working_dir,dirname_bin_32) + " " + compile_flags,shell=True)
call(r"ms\do_ms.bat",shell=True)
call(r"nmake -f ms\nt.mak",shell=True)
call(r"nmake -f ms\nt.mak instalL",shell=True)
print("32-bit compilation complete.")
#Go back to base dir
os.chdir(working_dir)
################
if arch == "64":
#extract for 64
call("7z x " + openssl_tar_file)
os.rename(dirname, dirname_src_64)
#Compile 64
os.chdir(dirname_src_64)
call("perl Configure " + openssl_64_flag + " --prefix=" + os.path.join(working_dir,dirname_bin_64) + " " + compile_flags,shell=True)
call(r"ms\do_ms.bat",shell=True)
call(r"nmake -f ms\nt.mak",shell=True)
call(r"nmake -f ms\nt.mak instalL",shell=True)
print("64-bit compilation complete.")
#Go back to base dir
os.chdir(working_dir)
################
os.remove(openssl_tar_file)
Option 1: Save the script to CompileOpenSSL.py, and download the OpenSSL source file that is expected to have the name format openssl-1.X.Y.tar.gz. Now assuming that 7zip and perl are accessible from the global scope on your command prompt and you have the correct MSVC variables loaded (with e.g. vsvars32.bat, or starting the right terminal), run the following:
python CompileOpenSSL.py openssl-1.X.Y.tar.gz 32
If you're using MSVC 32-bit, or
python CompileOpenSSL.py openssl-1.X.Y.tar.gz 64
for MSVC 64-bit.
Option 2: Do what the script does manually. The script simply extracts the archive, configures the sources and runs do_ms.bat then nmake. Follow the source and it'll work.
Good luck!
go into ssl directory using visual studio cmd and add perl and nasm to system path then type:
perl Configure --openssldir=D:OpenSSLdirectory VC-WIN32
ms\do_ms.bat
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak install
( enjoy. )

How to use jbuild and ppx_driver with ppx_deriving

I am trying to use jbuilder together with ppx_deriving (ppx_deriving_yojson specifically) but got stuck for well over an hour now. My current approach is a jbuild file, containing the following:
(jbuild_version 1)
(executables
((names (my-binary))
(libraries
(ppx_deriving
ppx_deriving_yojson
cohttp
yojson))
(preprocess (pps (ppx_deriving_yojson ppx_driver.runner)))))
But that results in
Command [5] exited with code 1:
$ (cd _build/default && ../.ppx/default/ppx_deriving_yojson+ppx_driver.runner/ppx.exe --dump-ast -o src/my_file.pp.ml --impl src/my_file.ml)
File "src/my_file.ml", line 16, characters 5-13:
Error: Attribute `deriving' was not used
Running the generated ppx_driver in _build/.ppx/default/ppx_deriving_yojson+ppx_driver.runner/ppx.exe manually with -print-transformations gives empty output, so I am obviously missing something.
The code builds fine with topkg by just including ppx_deriving and ppx_deriving_yojson as dependencies.
As of more recent versions of ppx_deriving_yojson this should be possible.
Code:
type t = {x: int; y: int} [##deriving to_yojson]
let () = print_endline (Yojson.Safe.to_string (to_yojson {x= 1; y= 2}))
And a sample jbuild file:
(jbuild_version 1)
(executables
((names (main))
(preprocess (pps (ppx_deriving_yojson)))
(libraries (ppx_deriving_yojson.runtime))))
(install
((section bin)
(files ((main.exe as main)))))

Dynamically determine all solutions, projects and configurations on a build/trigger event and call them all with MSBuild from command line/script

We're still a little early in setting up our build automation and just using a bat file for now for Win32 C++ solutions. We have about 4 solutions and each has a couple vcproj files.
Each time a new solution or configuration is added I have to update the bat file to reflect the new solution or configuration to call with MSBuild.
I thought perhaps it would be easier to write a tool that parses all the sln files in a given path (and its subtree) and then parse all project files it references for configurations and then call all the builds that way.
Is there an easy way to do this?
Really this is 2 questions:
How can I tell MSBuild just to build all solutions inside a subtree? (I can do a search on them - that is a simple tool I think to write)
How can I tell MSBuild to build all configurations of a solution/vcproj?
We're using MSBuild, bat files, vc2008, c++
Being a PowerShell fan the following reformatted one-liner might be of help:
Get-ChildItem -Recurse -Include *.sln |
ForEach-Object {
$Solution = $_.FullName
Get-Content $_ |
ForEach-Object {
if($_ -match '\{[^\}]+[^=]+= ([^\{\s]*)$') {
$matches[1]
}
} | Sort-Object -Unique |
ForEach-Object {
$config = ([string]$_).Split([char]'|')
& "$env:windir\Microsoft.NET\Framework\v3.5\msbuild.exe" $Solution /p:Configuration="$($config[0])" /p:Platform="$($config[1])"
}
}
This script can be saved as a ps1 file or pasted as a function in your profile. To explain what it does:
find all .sln files in and below the current directory
parse the sln extracting the configuration and platform values
for each unique configuration platform combination call msbuild with the given solution
--edit: Forgot Split-String is part of PSCX, it's better to use [string]
I hope this helps. If you still would like to use MsBuild, it does support a recursive form of the file glob operator via the ".\**\*.sln" syntax see here for details. MsBuild also provides the MsBuild task which can be used to build a set of solutions. I do not know how you can get all 'known' configurations from a solution easily in msbuild. Therefore I choose the PowerShell route.
The Microsoft build framework can be accessed via APIs. But i am not sure if one can do it only via a batch file. IMHO the easiest way is to automate the build by writing an addin (using VB.NET perhaps) to the VS IDE. This addin can be invoked from a batch file. We have automated VC++ builds like this using the addin approach. In our case there was no other way because we had to automate the moc process of Qt also. I think it will be more flexible also.
The build system (atleast in VS 2005) that i worked on was quite buggy. Refer to this link, it will help you in understanding the pitfalls and also provides code-snippets on how to traverse the solution files.
Once the addin is installed, a small exe can be created which invokes the build via addin. This exe can inturn be called from a batch file.
Sample VS Automator exe that calls addin methods
''' Summary: Console App to automate MSVS build with special regard to Addin
Imports System
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80
Imports System.Reflection
Imports System.Windows.Forms
Imports System.IO
'''<summary>Module class that automates launching MSVS in silent mode. It disables all user actions. A hidden window is used for MSVS automation. This should work for VS 2008 as well.</summary>
'''<remarks>An STA Thread is used for this console app. Refer http://msmvps.com/blogs/carlosq/archive/2007/10/11/frustrations-with-command-line-add-ins-for-visual-studio.aspx for details </remarks>
Module VSAutomaton
Private Enum VisualStudioSolVersion
Unknown = 0
VSNET2002 = 1
VSNET2003 = 2
VS2005 = 3
VS2008 = 4
End Enum
<STAThread()> _
Sub Main(ByVal args() As String)
Const ADDIN_PROGID As String = "AddIn-Name.Connect"
Const ADDIN_METHOD As String = "SyncSolutionBatch" ' name of your method in addin code
Dim dte As EnvDTE.DTE = Nothing
Dim dteType As Type
Dim commandLineAddIn As AddIn-Name.Connect = Nothing
Dim solutionFullFileName As String
Dim solutionFolder As String
Dim solutionName As String
Dim logFullFileName As String
Dim buildLogFile As String = Nothing
Dim buildConfig As String = Nothing
Dim connectObject As Object = Nothing
Dim connectObjectType As Type
Dim version As VisualStudioSolVersion
Dim progID As String
Dim executableName As String
Dim addIn As EnvDTE.AddIn
Dim msgFilter As MessageFilter.MessageFilter = Nothing
Try
msgFilter = New MessageFilter.MessageFilter
If args.Length = 0 Then
executableName = IO.Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly.Location)
ReportError("Usage: " & executableName & " solution_file_name.sln")
Else
solutionFullFileName = args(0) ' project solution file
If Not IO.File.Exists(solutionFullFileName) Then
ReportError("Solution file '" & solutionFullFileName & "' does not exist.")
Else
solutionFolder = IO.Path.GetDirectoryName(solutionFullFileName)
solutionName = IO.Path.GetFileNameWithoutExtension(solutionFullFileName)
logFullFileName = IO.Path.Combine(solutionFolder, solutionName & ".log")
If IO.File.Exists(logFullFileName) Then
IO.File.Delete(logFullFileName)
End If
version = GetSolutionVersion(solutionFullFileName)
If version = VisualStudioSolVersion.Unknown Then
ReportError("The format version of the solution file is not supported.")
Else
progID = GetVisualStudioProgID(version)
dteType = System.Type.GetTypeFromProgID(progID)
If dteType Is Nothing Then
ReportError("Could not find the ActiveX Server for ProgID '" & progID & "'. Likely the proper version of Visual Studio is not installed.")
Else
dte = DirectCast(System.Activator.CreateInstance(dteType), EnvDTE.DTE)
dte.SuppressUI = True
dte.UserControl = False
addIn = GetAddInByProgID(dte, ADDIN_PROGID)
If addIn Is Nothing Then
ReportError("The Add-in " & ADDIN_PROGID & " was not found in Visual Studio.")
Else
addIn.Connected = True
connectObject = addIn.Object
connectObjectType = connectObject.GetType
' So a copy of the same DLL is necessary in the same dir as this app. exe
connectObjectType.InvokeMember(ADDIN_METHOD, Reflection.BindingFlags.InvokeMethod Or Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public, Nothing, connectObject, New String() {solutionFullFileName})
End If
End If
End If
End If
End If
Catch ex As Exception
ReportError(ex.ToString)
Finally
If Not (dte Is Nothing) Then
Try
dte.Quit()
Catch ex As Exception
End Try
End If
If Not (msgFilter Is Nothing) Then
' this is a tricky aspect. We do not want leaks but .NET can sometimes be extra smart
msgFilter.Dispose() 'If the GC decides to re-collect the garbage from this app, then a crash may result
' but this is the drawback of indeterministic destruction semantics
End If
End Try
End Sub
Private Sub ReportError(ByVal msg As String)
#If DEBUG Then
MsgBox(msg)
#End If
Console.WriteLine(msg)
End Sub
Private Function GetAddInByProgID(ByVal dte As EnvDTE.DTE, ByVal addinProgID As String) As EnvDTE.AddIn
Dim addinResult As EnvDTE.AddIn = Nothing
Dim addin As EnvDTE.AddIn
For Each addin In dte.AddIns
If addin.ProgID = addinProgID Then
addinResult = addin
Exit For
End If
Next
Return addinResult
End Function
Private Function GetSolutionVersion(ByVal solutionFullFileName As String) As VisualStudioSolVersion
Dim version As VisualStudioSolVersion = VisualStudioSolVersion.Unknown
Dim solutionStreamReader As IO.StreamReader = Nothing
Dim firstLine As String = Nothing
Dim format As String
Try
solutionStreamReader = New IO.StreamReader(solutionFullFileName)
firstLine = solutionStreamReader.ReadLine()
format = firstLine.Substring(firstLine.LastIndexOf(" ")).Trim
Select Case format
Case "7.00"
version = VisualStudioSolVersion.VSNET2002
Case "8.00"
version = VisualStudioSolVersion.VSNET2003
Case "9.00"
version = VisualStudioSolVersion.VS2005
Case "10.00"
version = VisualStudioSolVersion.VS2008
End Select
Finally
If Not (solutionStreamReader Is Nothing) Then
solutionStreamReader.Close()
End If
End Try
Return version
End Function
Private Function GetVisualStudioProgID(ByVal version As VisualStudioSolVersion) As String
Dim progID As String = ""
Select Case version
Case VisualStudioSolVersion.VSNET2002
progID = "VisualStudio.DTE.7"
Case VisualStudioSolVersion.VSNET2003
progID = "VisualStudio.DTE.7.1"
Case VisualStudioSolVersion.VS2005
progID = "VisualStudio.DTE.8.0"
Case VisualStudioSolVersion.VS2008
progID = "VisualStudio.DTE.9.0"
End Select
Return progID
End Function
End Module
Sample batch file to invike the VS automator exe:
#echo off
:: --Usage: $>BatchFileName.bat "<project_name(.sln)>" {Release | Debug} [ Make ]
:: Please remember the "double-quotes".
REM -- check for blank input
if x%1%x == xx goto InputError
if x%2%x == xx goto InputError
echo Automating MSVS-Build for %1% ...
echo .
set arg1=%1%
REM -- remove quotes
for /f "useback tokens=*" %%a in ('%arg1%') do set match=%%~a
set slnFile=%match:~-4%
if %slnFile% == .sln goto lbl_FileOK
:lbl_FileOK
REM build configuration and output file
set SOLFILE=%1%
set BUILDCONFIG=%2%
set CLEANWSOBJS=%3%
REM -- Read necessary registry entries
REM --- Read MSVS installation dir
regedit /e A$B$C$.bxt "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Setup\VS"
find "VS7CommonDir" <A$B$C$.bxt>A$B$C$.bat
goto :1st
:1st
for /F "tokens=1* delims==" %%A in ('TYPE A$B$C$.bat ^| find "VS7CommonDir"') do set vscomdir=%%B
set vscomdir=%vscomdir:"=%
REM -- Initialize the MSVS environment
set VSENV="%vscomdir%Tools\vsvars32.bat"
call %VSENV% > nul
REM -- remove quotes
for /f "useback tokens=*" %%a in ('%SOLFILE%') do set str=%%~a
set LastFolder=%str%
REM -- Extract the project name
if "%LastFolder:~-1%"=="\" set LastFolder=%LastFolder:~0,-1%
for %%a in ("%LastFolder%") do set LastFolder=%%~nxa
set flname=%LastFolder:.shared=.sln%
set tmpfile=%solPath%\%flname%
REM --- Check if the target '.sln' already exists, if yes delete
if EXIST %NEWSOLFILE% DEL /Q %NEWSOLFILE%
REM -- use the addin functionality
VSAutomator.exe %SOLFILE%
REM --- create log file as projectname_buildconfig.log
set tmplog=%NEWSOLFILE:.sln=%
set OUTLOGFILE=%tmplog%_%BUILDCONFIG%.log
REM -- Now build the newly ready .sln file
echo .
echo Building Solution file - %NEWSOLFILE%, Output log file - %OUTLOGFILE%
echo .
if x%CLEANWSOBJS%x == xMakex goto JustBuild1
devenv.com /useenv %NEWSOLFILE% /CLEAN %BUILDCONFIG% /OUT %OUTLOGFILE% > nul
:JustBuild1
devenv.com /useenv %NEWSOLFILE% /BUILD %BUILDCONFIG% /OUT %OUTLOGFILE% > nul
Please remember that the code presented above may not be perfect as i have referred it from my POC that i did when i had a similar automation problem.