Understanding Property Sheet In Visual Studio Project Management

Naive Editing Properties

The naive method to manage a Visual-Studio project for different configurations (configuration + platform, such as Debug+x64) is to modify values directly in the Property Editor for each configuration. The disadvantages are

  • duplication in multi-configurations, which is difficult to maintain the consistency.
  • difficult to apply to multiple users, or multiple similar projects.

The appropriated solution is to use customized property sheets, which support cascade overriding.

Two Ways to Access Property Editor

  • naive: from project explorer, you can access properties for different configurations
  • advanced: from property manager. Upper sheet in each configuration overrides values in lower sheet. For VS2015, the global sheets locate at <drive>\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140, user sheets locates at <userprofile>\AppData\Local\Microsoft\MSBuild\v4.0. User sheets are obsolete, thus should be avoid to use (recommend to delete them from projects).


In the Property Manager, you can create/add any user custom property sheets at project level (that applies to all configuration) or at specified configuration.


Project Property Sheet

Custom property sheets are saved in stand-alone XML files (for example, MyProps4All.props), instead of project files (.vcxproj). The advantage of stand-alone property sheet files is that they can be shared by different projects and configurations via importing.

The property inheritance (or override order) is

  1. Default settings from the MSBuild CPP Toolset (..\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.Cpp.Default.props, which is imported by the .vcxproj file.)
  2. Property sheets
  3. .vcxproj file. (Can override the default and property sheet settings.)
  4. Items metadata






Integrate Python and C/C++ (1)

In python official document there are two chapters with the topic of integrating python and C/C++

  • Extending and Embedding – tutorial for C/C++ programmer
  • Python/C API

In addition, there are several open-source utilities that make programmers’ lives easier, the most popular ones are

  • SWIG
  • Boost.Python/Pyl11
  • Cython
  • CFFI

This series posts explain the practical experience of the integration of python and C/C++.

Create a simple python module with C

The Python document gives a very simple example (spam), with full source code and detail explanation. What is missing is how you run on a real computer and what the output looks like.

Source Code

Here is the full source code with some concise comment

/* module method

 the name does not matter, and should be static, because the method
 is exposed by its implementation name, it is by method-define-array
 (see below)

 as practical convention, make your function name as module_method_name

static PyObject*
spam_system(PyObject *self, PyObject *args)
 const char* command;

 int rc;

 if (!PyArg_ParseTuple(args, "s", &command))
 return NULL;

 rc = system(command);
 return Py_BuildValue("i", rc);

 An array of PyMethodDef is passed to module initializer.
 This array defines all methods, each item is

 { name, function_pointer, argument_type, description }

 This array should also be static (no need to be exposed)
static PyMethodDef SpamMethods[] = {
 { "system", spam_system, METH_VARARGS, "Execute a shell command." },

 // other methods

 // end of list

 Each should have one (and only one) module initializer, that introduces
 module objects into python namespace.

 Its name MUST BE init.

 For example if the name is changed to init_spam, it still can be
 compiled but python can't import it:

python test.py echo hello
Traceback (most recent call last):
 File "test.py", line 4, in 
 import spam
ImportError: dynamic module does not define init function (initspam)

 PyObject *m;

 m = Py_InitModule("spam", SpamMethods);
 if (m == NULL)

Manually build (on linux)

The document doesn’t mention the build process, instead it recommends to use distutils module. It is true, however, for curiosity I still decide to try to build manually. Here is the make file

$ cat manual.mk
# use pkg-config to find python information
# pkg-config --list-all
# pkg-config --cflags --libs python2
# -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7 -lpython2.7
CFLAGS = -g -fPIC -I/usr/include/python2.7
LDFLAGS = -g -shared -fPIC -L/usr/lib/python2.7
LIBS = -lpython2.7

all: spam.so

spam.o: spam.c
        $(CC) -c $(CFLAGS) -o $@ $<

spam.so: spam.o
        $(LD) -o $@ $< $(LDFLAGS) $(LIBS)

        rm spam.so spam.o

The build process

$ make -f manual.mk
cc -c -g -fPIC -I/usr/include/python2.7 -o spam.o spam.c
ld -o spam.so spam.o -g -shared -fPIC -L/usr/lib/python2.7 -lpython2.7

The python script that tests our new module spam

$ cat test.py
#! /usr/bin/env python

import sys,os
import spam

cmd = ' '.join(sys.argv[1:])
print 'cmd=[{}]'.format(cmd)
rc = spam.system(cmd)
print 'rc={:x}'.format(rc)

And the result of running.

$ python test.py hello
sh: 1: hello: not found
$ python test.py echo hello
cmd=[echo hello]

Proper method of building (on linux)

As python document recommends, it is much easier (and more standard and flexible) to build with distutils module

$ cat setup.py
#from distutils.core import setup, Extension
# distutils.core is obsolete, use setuptools instead
from setuptools import setup, Extension

# a package consists of one or more modules, each module consists of
# one or more source files
# this is module spam, built by source: spam.c
module1 = Extension('spam', sources = ['spam.c'])

# this is a package spam, consist of module1
      description='This is a demo package',

The process that run with distutils

$ python setup.py build
running build
running build_ext
building 'spam' extension
creating build
creating build/temp.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c spam.c -o build/temp.linux-x86_64-2.7/spam.o
creating build/lib.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wl,-z,relro -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/spam.o -o build/lib.linux-x86_64-2.7/spam.so
jasonz@jzdebian $ find build

Compare the output of the two build method

$ ls -l spam.o build/lib.linux-x86_64-2.7/spam.so
-rwxr-xr-x 1 jasonz jasonz 17304 Nov 30 13:46 build/lib.linux-x86_64-2.7/spam.so
-rw-r--r-- 1 jasonz jasonz 16752 Nov 30 13:49 spam.o
$ file spam.o build/lib.linux-x86_64-2.7/spam.so
spam.o:                             ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
build/lib.linux-x86_64-2.7/spam.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=9cdc9b1ebf2df10d243fbd931ecea38f3bd63dfc, not stripped

Build with distutils on Windows

Python 3.5 and later support VS2015 and later. For Python 2.7, Microsoft offers a special version of VC++, which is actually an engine of VC++2008.


  1. Python 2.7 for Windows is built by VC2008, this can be checked by launching python interpreter and check its build info, which should by MSC v.1500
  2. Without VC2008, setup.py auto detect and complains error “Unable to find vcvarsall.bat“.
  3. some old version of setup.py uses module distutils.core, which lacks the auto-detect ability, replace it with setuptools
  4. Microsoft requests to update setuptools to version 6.0 or later for letting the special version VC++ works
  5. download setuptools 6.0.1 ez_setup.py
  6. run command python ez_setup.py
Here is the process of build module on Windows.
D:\codex\python\ext>python setup.py build
running build
running build_ext
building 'spam' extension
creating build
creating build\temp.win-amd64-2.7
creating build\temp.win-amd64-2.7\Release
C:\Users\jasonz\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -IC:\Python27\include -IC:\Python27\PC /Tcspam.c /Fobuild\tem
creating build\lib.win-amd64-2.7
C:\Users\jasonz\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 /LIBPA
TH:C:\Python27\PC\VS9.0\amd64 /EXPORT:initspam build\temp.win-amd64-2.7\Release\spam.obj /OUT:build\lib.win-amd64-2.7\spam.pyd /IMPLIB:build\temp.win-amd64-2.7\Release\spam.lib /MANIFESTFILE:build\tem
spam.obj : warning LNK4197: export 'initspam' specified multiple times; using first specification
   Creating library build\temp.win-amd64-2.7\Release\spam.lib and object build\temp.win-amd64-2.7\Release\spam.exp

D:\codex\python\ext>python test.py echo hello
cmd=[echo hello]

D:\codex\python\ext>python test.py  hello
'hello' is not recognized as an internal or external command,
operable program or batch file.


Parse Command Line arguments


OPTIND=1           # reset in case getopts has been used previously
# default settings
while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)  show_help; exit 1;;
    v) verbose=1;;
    f) output=$OPTARG;;

shift $((OPTIND-1))
[ "$1" = "--" ] && shift;
echo "verbose=$verbose, output=$output, leftover: $@"



#! /usr/bin/env perl

use strict;
use warnings;
use Getopt::Std;

sub usage {
    print "usage:\n".
          "$0 -hvf other_list\n".
          "  -h     help\n".
          "  -v     verbose\".
          "  -f file output file\n".
    exit 1;

my %opts;
my $rc = getopts('hvf:', \%opts);
usage() if ($rc || $opts{h});
print "options:\n";
print "$_: $opts{$_}\n" foreach (sort keys %opts);
print "leftovers: @ARGV\n";



#! /usr/bin/env python

import argparse

parser = argparse.ArgumentParser()
parser.ad_argument('-f', '--output_file', type=str,
                     default='my_output', help='output file name')
parser.add_argument('-v', '--verbose', action='store_true', help='verbose mode')
args = parser.parse_args()
if args.verbose:
    print 'verbose mode'
with open(args.output_file, 'w') as f:
    # do something



under construction

Setup LLVM+Clang

  1. go to http://clang.llvm.org, download latest source: llvm, clang, compiler-rt
  2. extract 3 packages, put clang and compiler-rt inside llvm
  3. build the bundle
  4. install binary
  5. install clang python binding
mkdir -p ~/pkg/llvm # make root of llvm-bundle
cd ~/pkg/llvm
tar --strip-components=1 \
     -xJf ~/Downloads/llvm-3.8.0.src.tar.xz
mkdir -p tools/clang # extract clang here
tar --strip-components=1 -C tools/clang \
     -xJf ~/Downloads/cfe-3.8.0.src.tar.xz
mkdir -p projects/compiler-rt
tar --strip-components=1 -C projects/compiler-rt \
     -xJf ~/Downloads/compiler-rt-3.8.0.src.tar.xz
du -s tools/clang projects/compiler-rt .
600340  tools/clang
122568  projects/compiler-rt
3467256 .

# shadow building: direct building is not supported
mkdir ../llvm.build
cd ../llvm.build
# default build type is Debug
cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ../llvm
script build.log
time make

du -s bin include lib .
478136  bin
8464    include
717108  lib
273488  .

tail build.log

real    86m26.808s
user    80m30.736s
sys     5m2.292s

# install bin/include/lib to /usr/local
sudo make install
# include clang python bindings
cd ../llvm
cp -r tools/clang/bindings/python/clang \