#!/usr/bin/env ''' wscript - Waf build script for NORM See https://gitlab.com/ita1024/waf/ for more information. In order to use different build directories (for example, a release and a debug build), use the -o (--out) flag when configuring. For example: ./waf configure -o build-debug --debug ./waf ./waf configure -o build-release ./waf To build examples, use the --target directive. For example: ./waf build --target=normClient,normServer ''' import platform import waflib import sys import os # Fetch VERSION from include/normVersion.h file VERSION = None vfile = open('include/normVersion.h', 'r') for line in vfile.readlines(): line = line.split() if len(line) != 3: continue if "#define" == line[0] and "VERSION" == line[1]: VERSION = line[2].strip('"') if VERSION is None: print ("Warning: NORM VERSION not found!?") # So you don't need to do ./waf configure if you are just using the defaults waflib.Configure.autoconfig = True # Top-level project directory top = '.' # Directory where build files are placed out = 'build' # System waf is running on: linux, darwin (Mac OSX), freebsd, windows, etc. system = platform.system().lower() def options(ctx): ctx.recurse('protolib') build_opts = ctx.add_option_group('Compile/install Options', 'Use during build/install step.') # Add Rust binding options ctx.add_option('--build-rust', action='store_true', default=False, help='Build Rust bindings for NORM') ctx.add_option('--rust-release', action='store_true', default=False, help='Build Rust bindings in release mode') ctx.add_option('--rust-docs', action='store_true', default=False, help='Generate documentation for Rust bindings') def configure(ctx): ctx.recurse('protolib') # Configure Rust bindings if requested if ctx.options.build_rust: try: ctx.find_program('cargo', var='CARGO', mandatory=True) ctx.env.BUILD_RUST = True ctx.env.RUST_RELEASE = ctx.options.rust_release ctx.env.RUST_DOCS = ctx.options.rust_docs except ctx.errors.ConfigurationError: ctx.fatal('Rust toolchain not found. Install Rust from https://rustup.rs') # Use this USE variable to add flags to NORM's compilation ctx.env.USE_BUILD_NORM += ['BUILD_NORM'] if system in ('linux', 'darwin', 'freebsd', 'gnu', 'gnu/kfreebsd'): ctx.env.DEFINES_BUILD_NORM += ['ECN_SUPPORT'] #if system == 'windows': # ctx.env.DEFINES_BUILD_NORM += ['NORM_USE_DLL'] if ctx.env.COMPILER_CXX == 'g++' or ctx.env.COMPILER_CXX == 'clang++': ctx.env.CFLAGS += ['-fvisibility=hidden', '-Wno-attributes'] ctx.env.CXXFLAGS += ['-fvisibility=hidden', '-Wno-attributes'] #if 'darwin' == system: # ctx.env.LINKFLAGS += ['-L/opt/local/lib'] # Will be used by the pkg-config generator ctx.env.VERSION = VERSION def build(ctx): ctx.recurse('protolib') # Build Rust bindings if configured if hasattr(ctx.env, 'BUILD_RUST') and ctx.env.BUILD_RUST: # Import our custom Rust build module sys.path.insert(0, os.path.join(ctx.path.abspath(), 'src/rust')) try: import waf_rust waf_rust.build_rust_bindings(ctx) except ImportError: ctx.fatal('Failed to import Rust waf module. Check src/rust/waf_rust.py exists.') # Setup to install NORM header file ctx.install_files("${PREFIX}/include/", "include/normApi.h") ctx.objects( target = 'normObjs', includes = ['include', 'protolib/include'], export_includes = ['include'], use = ctx.env.USE_BUILD_NORM + ctx.env.USE_BUILD_PROTOLIB, source = ['src/common/{0}.cpp'.format(x) for x in [ 'galois', 'normEncoder', 'normEncoderMDP', 'normEncoderRS16', 'normEncoderRS8', 'normFile', 'normMessage', 'normNode', 'normObject', 'normSegment', 'normSession', ]], ) # Protolib is incorporated into static and dynmamic NORM libs ctx.shlib( target = 'norm', name = 'norm_shlib', includes = ['include'], export_includes = ['include'], use = ['protoObjs', 'normObjs'], defines = ['NORM_USE_DLL'] if 'windows' == system else [], vnum = VERSION, source = ['src/common/normApi.cpp'], features = 'cxx cxxshlib', install_path = '${LIBDIR}', ) ctx.stlib( target = 'norm' if 'windows' != system else 'norm_static', name = 'norm_stlib', includes = ['include'], export_includes = ['include'], use = ['protoObjs', 'normObjs'], vnum = VERSION, source = ['src/common/normApi.cpp'], features = 'cxx cxxstlib', install_path = '${LIBDIR}', ) if ctx.env.BUILD_PYTHON: ctx( use = ['norm_shlib'], features='py', source=ctx.path.ant_glob('src/pynorm/**/*.py'), install_from='src', ) if ctx.env.BUILD_JAVA: ctx.shlib( target = 'mil_navy_nrl_norm', includes = ['include'], use = ['norm_shlib', 'JAVA'], vnum = VERSION, defines = ['NORM_USE_DLL'] if 'windows' == system else [], source = ['src/java/jni/{0}.cpp'.format(x) for x in [ 'normJni', 'normInstanceJni', 'normSessionJni', 'normObjectJni', 'normDataJni', 'normFileJni', 'normStreamJni', 'normEventJni', 'normNodeJni', ]], ) ctx( features = ['javac', 'jar'], srcdir = 'src/java/src', outdir = 'src/java/src', basedir = 'src/java/src', destfile = 'norm.jar', ) # Links to static library since it uses C++ objects directly instead of API # Hack to force clang to statically link stuff if 'clang++' == ctx.env.COMPILER_CXX: use = ['protoObjs', 'normObjs'] source = ['src/common/normApi.cpp'] else: use = ['norm_stlib', 'protolib_st'] source = [] normapp = ctx.program( # Need to explicitly set a different name, because # the library is also named "norm" name = 'normapp', target = 'normapp', includes = ['include', 'protolib/include'], use = use, defines = [], source = source + ['src/common/{0}.cpp'.format(x) for x in [ 'normPostProcess', 'normApp', ]], # Disabled by default posted = True, ) if system in ('linux', 'darwin', 'freebsd', 'gnu', 'gnu/kfreebsd'): normapp.source.append('src/unix/unixPostProcess.cpp') if system == 'windows': normapp.source.append('src/win32/win32PostProcess.cpp') normapp.defines.append('_CONSOLE') normapp.stlib = (["Shell32"]); for example in ( #'normDataExample', 'normDataRecv', 'normDataSend', 'normFileRecv', 'normFileSend', 'normStreamRecv', 'normStreamSend', 'normMsgr', 'normStreamer', 'normCast', 'chant', 'normClient', 'normServer', #'wintest' # Windows only (can uncomment on Windows) ): _make_simple_example(ctx, example) for prog in ( 'fecTest', 'normPrecode', 'normTest', 'normThreadTest', 'raft', ): _make_simple_example(ctx, prog, 'src/common') # Enable example targets specified on the command line ctx._parse_targets() # Generate pkg-config file # Add additional static compilation dependencies based on the system. # libpcap is used by protolib on GNU/Hurd based systems. static_libs = '' if hasattr(ctx.options, 'enable_static_library'): if ctx.options.enable_static_library: static_libs += ' -lstdc++ -lprotokit' if system == "gnu": static_libs += ' -lpcap' ctx(source='norm.pc.in', STATIC_LIBS = static_libs) def _make_simple_example(ctx, name, path='examples'): '''Makes a task from a single source file in the examples directory. Note these tasks are not built by default. Use the waf build --targets flag. ''' # Hack to force clang to statically link stuff if 'clang++' == ctx.env.COMPILER_CXX: use = ['protoObjs', 'normObjs'] source = ['src/common/normApi.cpp'] else: use = ['norm_stlib', 'protolib_st'] source = [] source += ['{0}/{1}.cpp'.format(path, name)] if 'normClient' == name or 'normServer' == name: source.append('%s/normSocket.cpp' % path) if 'normCast' == name: source.append('src/common/normPostProcess.cpp') if system in ('linux', 'darwin', 'freebsd', 'gnu', 'gnu/kfreebsd'): source.append('src/unix/unixPostProcess.cpp') elif system == 'windows': source.append('src/win32/win32PostProcess.cpp') example = ctx.program( target = name, includes = ['include', 'protolib/include'], use = use, defines = [], source = source, # Don't build examples by default posted = True, # Don't install examples install_path = False, ) if 'windows' == system: example.defines.append('_CONSOLE') example.stlib = (["Shell32"])