2016-09-10 13:09:50 +02:00
#!/usr/bin/python
###############################################################################
# #
# This file is part of IfcOpenShell. #
# #
# IfcOpenShell is free software: you can redistribute it and/or modify #
# it under the terms of the Lesser GNU General Public License as published by #
# the Free Software Foundation, either version 3.0 of the License, or #
# (at your option) any later version. #
# #
# IfcOpenShell is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# Lesser GNU General Public License for more details. #
# #
# You should have received a copy of the Lesser GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
###############################################################################
# #
# This script builds IfcOpenShell and its dependencies #
# #
# Prerequisites for this script to function correctly: #
# * git * bzip2 * tar * c(++) compilers * yacc * autoconf #
2017-08-13 13:13:26 +02:00
# #
# if building with USE_OCCT additionally: #
# * freetype * glx.h #
# #
2016-09-10 13:09:50 +02:00
# on debian 7.8 these can be obtained with: #
# $ apt-get install git gcc g++ autoconf bison bzip2 #
2017-08-13 13:13:26 +02:00
# libfreetype6-dev mesa-common-dev #
# #
2016-09-10 13:09:50 +02:00
# on ubuntu 14.04: #
# $ apt-get install git gcc g++ autoconf bison make #
2017-08-13 13:13:26 +02:00
# libfreetype6-dev mesa-common-dev #
# #
2016-09-10 13:09:50 +02:00
# on OS X El Capitan with homebrew: #
2017-08-13 13:13:26 +02:00
# $ brew install git bison autoconf automake freetype #
2016-09-10 13:09:50 +02:00
# #
###############################################################################
import logging
import os
import sys
import subprocess as sp
import shutil
import time
import tarfile
2016-11-06 19:07:03 +01:00
import multiprocessing
2017-08-13 13:13:26 +02:00
import urllib
2016-09-10 13:09:50 +02:00
logger = logging . getLogger ( __name__ )
logger . setLevel ( logging . INFO )
ch = logging . StreamHandler ( )
ch . setLevel ( logging . INFO )
logger . addHandler ( ch )
PROJECT_NAME = " IfcOpenShell "
2017-03-09 14:36:00 +01:00
OCE_VERSION = " 0.18 "
2018-04-15 14:01:37 +02:00
# OCCT_VERSION="7.1.0"
# OCCT_HASH="89aebde"
2019-03-08 22:00:15 +08:00
PYTHON_VERSIONS = [ " 2.7.16 " , " 3.2.6 " , " 3.3.6 " , " 3.4.6 " , " 3.5.3 " , " 3.6.2 " ]
2018-08-19 10:29:01 +02:00
# OCCT_VERSION="7.2.0"
# OCCT_HASH="88af392"
OCCT_VERSION = " 7.3.0 "
2018-07-01 11:37:36 +02:00
BOOST_VERSION = " 1.59.0 "
2017-01-19 10:24:09 +01:00
PCRE_VERSION = " 8.39 "
2016-09-10 13:09:50 +02:00
LIBXML_VERSION = " 2.9.3 "
2018-07-01 11:37:36 +02:00
CMAKE_VERSION = " 3.4.1 "
2016-09-10 13:09:50 +02:00
ICU_VERSION = " 56.1 "
2017-10-30 10:26:27 +01:00
SWIG_VERSION = " 3.0.12 "
2016-09-10 13:09:50 +02:00
# binaries
2016-11-13 20:27:22 +01:00
cp = " cp "
bash = " bash "
2016-09-10 13:09:50 +02:00
uname = " uname "
git = " git "
bunzip2 = " bunzip2 "
tar = " tar "
cc = " cc "
cplusplus = " c++ "
autoconf = " autoconf "
automake = " automake "
yacc = " yacc "
make = " make "
date = " date "
curl = " curl "
wget = " wget "
strip = " strip "
# Helper function for coloured printing
NO_COLOR = " \033 [0m " # <ref>http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux</ref>
BLACK_ON_WHITE = " \033 [0;30;107m "
RED = " \033 [31m "
GREEN = " \033 [32m "
YELLOW = " \033 [33m "
MAGENTA = " \033 [35m "
def cecho ( message , color = NO_COLOR ) :
""" Logs message `message` in color `color`. """
2017-08-13 13:13:26 +02:00
logger . info ( " %s %s \033 [0m " % ( color , message ) )
2016-09-10 13:09:50 +02:00
def fullpath ( arg ) :
return os . path . realpath ( os . path . dirname ( sys . argv [ 1 ] ) )
def which ( cmd ) :
for path in os . environ [ " PATH " ] . split ( " : " ) :
if os . path . exists ( path ) and cmd in os . listdir ( path ) :
return cmd
return None
def get_os ( ) :
ret_value = sp . check_output ( [ uname , " -s " ] ) . strip ( )
return ret_value
# Set defaults for missing empty environment variables
2017-10-02 10:22:57 +02:00
USE_OCCT = os . environ . get ( " USE_OCCT " , " true " ) . lower ( ) == " true "
TOOLSET = None
if get_os ( ) == " Darwin " :
# C++11 features used in OCCT 7+ need a more recent stdlib
TOOLSET = " 10.9 " if USE_OCCT else " 10.6 "
2016-09-10 13:09:50 +02:00
try :
IFCOS_NUM_BUILD_PROCS = os . environ [ " IFCOS_NUM_BUILD_PROCS " ]
except KeyError :
2016-11-06 19:07:03 +01:00
IFCOS_NUM_BUILD_PROCS = multiprocessing . cpu_count ( ) + 1
2016-09-10 13:09:50 +02:00
os . environ [ " IFCOS_NUM_BUILD_PROCS " ] = str ( IFCOS_NUM_BUILD_PROCS )
try :
2016-11-09 11:12:29 +01:00
TARGET_ARCH = os . environ [ " TARGET_ARCH " ]
del os . environ [ " TARGET_ARCH " ]
2016-09-10 13:09:50 +02:00
except KeyError :
TARGET_ARCH = sp . check_output ( [ uname , " -m " ] ) . strip ( )
CMAKE_DIR = os . path . realpath ( os . path . join ( " .. " , " cmake " ) )
try :
DEPS_DIR = os . environ [ " DEPS_DIR " ]
except KeyError :
2017-10-02 10:22:57 +02:00
path = [ " .. " , " build " , sp . check_output ( uname ) . strip ( ) , TARGET_ARCH ]
if TOOLSET :
path . append ( TOOLSET )
DEPS_DIR = os . path . realpath ( os . path . join ( * path ) )
2016-09-10 13:09:50 +02:00
os . environ [ " DEPS_DIR " ] = DEPS_DIR
if not os . path . exists ( DEPS_DIR ) :
os . makedirs ( DEPS_DIR )
try :
2017-02-16 15:53:58 +01:00
BUILD_CFG = os . environ [ " BUILD_CFG " ]
2016-09-10 13:09:50 +02:00
except KeyError :
BUILD_CFG = " RelWithDebInfo "
os . environ [ " BUILD_CFG " ] = BUILD_CFG
# Print build configuration information
cecho ( """ This script fetches and builds %s and its dependencies
""" % ( PROJECT_NAME , ) , BLACK_ON_WHITE )
cecho ( """ Script configuration:
""" , GREEN )
cecho ( """ * Target Architecture = %s """ % ( TARGET_ARCH , ) , MAGENTA )
cecho ( " - Whether 32-bit (i686) or 64-bit (x86_64) will be built. " )
2017-08-13 13:13:26 +02:00
cecho ( """ * USE_OCCT = %r """ % ( USE_OCCT , ) , MAGENTA )
if USE_OCCT :
cecho ( " - Compiling against official Open Cascade " )
else :
cecho ( " - Compiling against Open Cascade Community Edition " )
2016-09-10 13:09:50 +02:00
cecho ( " * Dependency Directory = %s " % ( DEPS_DIR , ) , MAGENTA )
cecho ( " - The directory where %s dependencies are installed. " % ( PROJECT_NAME , ) )
cecho ( " * Build Config Type = %s " % ( BUILD_CFG , ) , MAGENTA )
cecho ( """ - The used build configuration type for the dependencies.
Defaults to RelWithDebInfo if not specified. """ )
if BUILD_CFG == " MinSizeRel " :
cecho ( " WARNING: MinSizeRel build can suffer from a significant performance loss. " , RED )
cecho ( " * IFCOS_NUM_BUILD_PROCS = %s " % ( IFCOS_NUM_BUILD_PROCS , ) , MAGENTA )
cecho ( """ - How many compiler processes may be run in parallel.
""" )
# Check that required tools are in PATH
2018-04-15 14:01:37 +02:00
for cmd in [ git , bunzip2 , tar , cc , cplusplus , autoconf , automake , yacc , make , " patch " ] :
2016-09-10 13:09:50 +02:00
if which ( cmd ) is None :
raise ValueError ( " Required tool ' %s ' not installed or not added to PATH " % ( cmd , ) )
# identifiers for the download tool (could be less memory consuming as ints, but are more verbose as strings)
download_tool_curl = " curl "
download_tool_wget = " wget "
download_tool_git = " git "
2017-09-18 14:51:58 +02:00
2016-09-10 13:09:50 +02:00
if which ( wget ) != None :
download_tool_default = download_tool_wget
elif which ( curl ) != None :
download_tool_default = download_tool_curl
else :
raise ValueError ( " No download application found, tried: curl, wget " )
2017-09-18 14:51:58 +02:00
CURL = [ " curl " , " -sL " ]
2016-09-10 13:09:50 +02:00
WGET = [ " wget " , " -q " , " --no-check-certificate " ]
# Create log directory and file
log_dir = os . path . join ( DEPS_DIR , " logs " )
if not os . path . exists ( log_dir ) :
os . makedirs ( log_dir )
LOG_FILE = " %s .log " % ( os . path . join ( log_dir , sp . check_output ( [ date , " + % Y % m %d " ] ) . strip ( ) ) , )
if not os . path . exists ( LOG_FILE ) :
open ( LOG_FILE , " w " ) . close ( )
logger . info ( " using command log file ' %s ' " % ( LOG_FILE , ) )
def __check_call__ ( cmds , cwd = None ) :
2016-11-13 20:27:22 +01:00
logger . debug ( " running command %r in directory %r " % ( " " . join ( cmds ) , cwd ) )
2016-09-10 13:09:50 +02:00
log_file_handle = open ( LOG_FILE , " a " )
2016-11-06 19:07:03 +01:00
proc = sp . Popen ( cmds , cwd = cwd , stdout = log_file_handle , stderr = sp . PIPE )
_ , stderr = proc . communicate ( )
log_file_handle . write ( stderr )
2016-09-10 13:09:50 +02:00
log_file_handle . close ( )
2016-11-06 19:07:03 +01:00
if proc . returncode != 0 :
print " - " * 70
print stderr
print " - " * 70
raise Exception ( " Command ` %s ` returned exit code %d " % ( " " . join ( cmds ) , proc . returncode ) )
2016-09-10 13:09:50 +02:00
def __check_output__ ( cmds , cwd = None ) :
""" Wraps `subprocess.check_output` and logs the command being executed,
sets up logging `stderr` to `LOG_FILE` (in append mode) and strips the
return value because it ' s unlikely that the newline at the end of output is
useful and it often causes errors """
2016-11-13 20:27:22 +01:00
logger . debug ( " running command ' %s ' in directory %r " % ( " " . join ( cmds ) , cwd ) )
2016-09-10 13:09:50 +02:00
log_file_handle = open ( LOG_FILE , " a " )
ret_value = sp . check_output ( cmds , cwd = cwd , stderr = log_file_handle ) . strip ( )
2016-11-13 20:27:22 +01:00
logger . debug ( " command returned %r " % ret_value )
2016-09-10 13:09:50 +02:00
log_file_handle . close ( )
return ret_value
BOOST_VERSION_UNDERSCORE = BOOST_VERSION . replace ( " . " , " _ " )
ICU_VERSION_UNDERSCORE = ICU_VERSION . replace ( " . " , " _ " )
2018-07-01 09:16:34 +02:00
CMAKE_VERSION_2 = CMAKE_VERSION [ : CMAKE_VERSION . rindex ( ' . ' ) ]
2016-09-10 13:09:50 +02:00
OCE_LOCATION = " https://github.com/tpaviot/oce/archive/OCE- %s .tar.gz " % ( OCE_VERSION , )
BOOST_LOCATION = " http://downloads.sourceforge.net/project/boost/boost/ %s /boost_ %s .tar.bz2 " % ( BOOST_VERSION , BOOST_VERSION_UNDERSCORE )
OPENCOLLADA_LOCATION = " https://github.com/KhronosGroup/OpenCOLLADA.git "
2018-10-01 08:02:52 +08:00
#OPENCOLLADA_COMMIT="f99d59e73e565a41715eaebc00c7664e1ee5e628"
OPENCOLLADA_COMMIT = " v1.6.63 "
2016-09-10 13:09:50 +02:00
# Helper functions
def run_autoconf ( arg1 , configure_args , cwd ) :
configure_path = os . path . realpath ( os . path . join ( cwd , " .. " , " configure " ) )
if not os . path . exists ( configure_path ) :
__check_call__ ( [ bash , " ./autogen.sh " ] , cwd = os . path . realpath ( os . path . join ( cwd , " .. " ) ) ) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things
2016-11-06 19:07:03 +01:00
# Using `sh` over `bash` fixes issues with building swig
__check_call__ ( [ " /bin/sh " , " ../configure " ] + configure_args + [ " --prefix= %s " % ( os . path . realpath ( " %s /install/ %s " % ( DEPS_DIR , arg1 ) ) , ) ] , cwd = cwd )
2016-09-10 13:09:50 +02:00
def run_cmake ( arg1 , cmake_args , cmake_dir = None , cwd = None ) :
if cmake_dir is None :
P = " .. "
else :
P = cmake_dir
cmake_path = os . path . join ( DEPS_DIR , " install " , " cmake- %s " % ( CMAKE_VERSION , ) , " bin " , " cmake " )
2017-08-13 13:13:26 +02:00
__check_call__ ( [ cmake_path , P ] + cmake_args + [ " -DCMAKE_BUILD_TYPE= %s " % ( BUILD_CFG , ) ] , cwd = cwd )
2016-09-10 13:09:50 +02:00
def run_icu ( arg1 , icu_args , cwd ) :
PLATFORM = get_os ( )
if PLATFORM == " Darwin " :
PLATFORM = " MacOSX "
__check_call__ ( [ bash , " ../source/runConfigureICU " , PLATFORM ] + icu_args + [ " --prefix= %s /install/ %s " % ( DEPS_DIR , arg1 ) ] , cwd = cwd )
def git_clone ( clone_url , target_dir , revision = None ) :
""" Lazily clones the `git` repository denoted by `clone_url` into
`target_dir`, i.e. skips cloning if `target_dir` exists (naively assumes
that a working clone exists there) and optionally checks out a revision
`revision` after cloning or in the existing clone if `revision` is not
`None`. """
if not os . path . exists ( target_dir ) :
logger . info ( " cloning ' %s ' into ' %s ' " % ( clone_url , target_dir ) )
__check_call__ ( [ git , " clone " , clone_url , target_dir ] )
else :
logger . info ( " directory ' %s ' exists, skipping cloning " % ( target_dir , ) )
if revision != None :
__check_call__ ( [ git , " checkout " , revision ] , cwd = target_dir )
2018-04-15 14:01:37 +02:00
def build_dependency ( name , mode , build_tool_args , download_url , download_name , download_tool = download_tool_default , revision = None , patch = None , additional_files = { } , no_append_name = False ) :
2016-09-10 13:09:50 +02:00
""" Handles building of dependencies with different tools (which are
distinguished with the `mode` argument. `build_tool_args` is expected to be
a list which is necessary in order to not mess up quoting of compiler and
linker flags. """
check_dir = os . path . join ( DEPS_DIR , " install " , name )
if os . path . exists ( check_dir ) :
logger . info ( " Found existing %s , skipping " % ( name , ) )
return
build_dir = os . path . join ( DEPS_DIR , " build " )
if not os . path . exists ( build_dir ) :
os . makedirs ( build_dir )
logger . info ( " \r Fetching %s ... " % ( name , ) )
2017-09-18 14:51:58 +02:00
if download_tool == download_tool_curl or download_tool == download_tool_wget :
if no_append_name :
url = download_url
else :
url = os . path . join ( download_url , download_name )
2016-09-10 13:09:50 +02:00
if download_tool == download_tool_curl :
download_path = os . path . join ( build_dir , download_name )
if not os . path . exists ( download_path ) :
2017-09-18 14:51:58 +02:00
__check_call__ ( CURL + [ " -o " , download_name , url ] , cwd = build_dir )
2016-09-10 13:09:50 +02:00
else :
logger . info ( " Download ' %s ' already exists, assuming it ' s an undamaged download and that it has been extracted if possible, skipping " % ( download_path , ) )
elif download_tool == download_tool_wget :
download_path = os . path . join ( build_dir , download_name )
if not os . path . exists ( download_path ) :
2017-09-18 14:51:58 +02:00
__check_call__ ( WGET + [ " -O " , download_name , url ] , cwd = build_dir )
2016-09-10 13:09:50 +02:00
else :
logger . info ( " Download ' %s ' already exists, assuming it ' s an undamaged download and that it has been extracted if possible, skipping " % ( download_path , ) )
elif download_tool == download_tool_git :
git_clone ( download_url , target_dir = os . path . join ( build_dir , download_name ) , revision = revision )
else :
raise ValueError ( " download tool ' %s ' is not supported " % ( download_tool , ) )
download_dir = os . path . join ( build_dir , download_name )
2017-08-13 13:13:26 +02:00
2016-09-10 13:09:50 +02:00
if os . path . isdir ( download_dir ) :
extract_dir_name = download_name
extract_dir = os . path . join ( build_dir , extract_dir_name )
else :
download_tarfile_path = os . path . join ( build_dir , download_name )
if download_name . endswith ( " .tar.gz " ) or download_name . endswith ( " .tgz " ) :
compr = " gz "
elif download_name . endswith ( " .tar.bz2 " ) :
compr = " bz2 "
else :
raise RuntimeError ( " fix source for new download type " )
download_tarfile = tarfile . open ( name = download_tarfile_path , mode = " r: %s " % ( compr , ) )
extract_dir_name = os . path . commonprefix ( download_tarfile . getnames ( ) ) # tarfile seriously doesn't have a function to retrieve the root directory more easily
#__check_output__([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works
if extract_dir_name is None :
extract_dir_name = __check_output__ ( [ bash , " -c " , " tar -tf %s 2> /dev/null | head -n 1 | cut -f1 -d / " % ( download_name , ) ] , cwd = build_dir )
extract_dir = os . path . join ( build_dir , extract_dir_name )
if not os . path . exists ( extract_dir ) :
__check_call__ ( [ tar , " -xf " , download_name ] , cwd = build_dir )
2017-08-13 13:13:26 +02:00
for path , url in additional_files . items ( ) :
if not os . path . exists ( path ) :
urllib . urlretrieve ( url , os . path . join ( extract_dir , path ) )
2018-04-15 14:01:37 +02:00
if patch is not None :
patch_abs = os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , patch ) )
if os . path . exists ( patch_abs ) :
try : __check_call__ ( [ " patch " , " -p1 " , " --batch " , " --forward " , " -i " , patch_abs ] , cwd = extract_dir )
except Exception as e :
# Assert that the patch has already been applied
__check_call__ ( [ " patch " , " -p1 " , " --batch " , " --reverse " , " --dry-run " , " -i " , patch_abs ] , cwd = extract_dir )
2016-09-10 13:09:50 +02:00
if mode != " bjam " :
extract_build_dir = os . path . join ( extract_dir , " build " )
if os . path . exists ( extract_build_dir ) :
shutil . rmtree ( extract_build_dir )
os . makedirs ( extract_build_dir )
logger . info ( " \r Configuring %s ... " % ( name , ) )
if mode == " icu " :
run_icu ( name , build_tool_args , cwd = extract_build_dir )
elif mode == " autoconf " :
run_autoconf ( name , build_tool_args , cwd = extract_build_dir )
elif mode == " cmake " :
run_cmake ( name , build_tool_args , cwd = extract_build_dir )
else :
raise ValueError ( )
logger . info ( " \r Building %s ... " % ( name , ) )
__check_call__ ( [ make , " -j %s " % ( IFCOS_NUM_BUILD_PROCS , ) ] , cwd = extract_build_dir )
logger . info ( " \r Installing %s ... " % ( name , ) )
__check_call__ ( [ make , " install " ] , cwd = extract_build_dir )
logger . info ( " \r Installed %s \n " % ( name , ) )
else :
logger . info ( " \r Configuring %s ... " % ( name , ) )
__check_call__ ( [ bash , " ./bootstrap.sh " ] , cwd = extract_dir )
logger . info ( " \r Building %s ... " % ( name , ) )
__check_call__ ( [ " ./b2 " , " -j %s " % ( IFCOS_NUM_BUILD_PROCS , ) ] + build_tool_args , cwd = extract_dir )
logger . info ( " \r Installing %s ... " % ( name , ) )
2017-01-09 13:10:01 +01:00
shutil . copytree ( os . path . join ( extract_dir , " boost " ) , os . path . join ( DEPS_DIR , " install " , " boost- %s " % BOOST_VERSION , " boost " ) )
2016-09-10 13:09:50 +02:00
logger . info ( " \r Installed %s \n " % ( name , ) )
cecho ( " Collecting dependencies: " , GREEN )
# Set compiler flags for 32bit builds on 64bit system
# TODO: This is untested
ADDITIONAL_ARGS = [ ]
BOOST_ADDRESS_MODEL = [ ]
if TARGET_ARCH == " i686 " and __check_output__ ( [ uname , " -m " ] ) . strip ( ) == " x86_64 " :
ADDITIONAL_ARGS = [ " -m32 " , " -arch i386 " ]
BOOST_ADDRESS_MODEL = [ " architecture=x86 " , " address-model=32 " ]
if get_os ( ) == " Darwin " :
2017-10-02 10:22:57 +02:00
ADDITIONAL_ARGS = [ " -mmacosx-version-min= %s " % TOOLSET ] + ADDITIONAL_ARGS
2016-09-10 13:09:50 +02:00
# If the linker supports GC sections, set it up to reduce binary file size
# -fPIC is required for the shared libraries to work
try :
CXXFLAGS = os . environ [ " CXXFLAGS " ]
except KeyError :
CXXFLAGS = " "
try :
CFLAGS = os . environ [ " CFLAGS " ]
except KeyError :
CFLAGS = " "
try :
LDFLAGS = os . environ [ " LDFLAGS " ]
except KeyError :
LDFLAGS = " "
2017-11-18 16:51:59 +01:00
2017-11-25 19:15:25 +01:00
if sp . call ( [ bash , " -c " , " ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null " ] ) != 0 :
2016-09-10 13:09:50 +02:00
CXXFLAGS_MINIMAL = " %s -fPIC %s " % ( CXXFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CXXFLAGS_MINIMAL " ] = CXXFLAGS_MINIMAL
CFLAGS_MINIMAL = " %s -fPIC %s " % ( CFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CFLAGS_MINIMAL " ] = CFLAGS_MINIMAL
CXXFLAGS = " %s -fPIC -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden %s " % ( CXXFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CXXFLAGS " ] = CXXFLAGS
CFLAGS = " %s -fPIC -fdata-sections -ffunction-sections -fvisibility=hidden %s " % ( CFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CFLAGS " ] = CFLAGS
LDFLAGS = " %s -Wl,--gc-sections %s " % ( LDFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " LDFLAGS " ] = LDFLAGS
else :
CXXFLAGS_MINIMAL = " %s -fPIC %s " % ( CXXFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CXXFLAGS_MINIMAL " ] = CXXFLAGS_MINIMAL
CFLAGS_MINIMAL = " %s -fPIC %s " % ( CFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CFLAGS_MINIMAL " ] = CFLAGS_MINIMAL
CXXFLAGS = " %s -fPIC -fvisibility=hidden -fvisibility-inlines-hidden %s " % ( CXXFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CXXFLAGS " ] = CXXFLAGS
CFLAGS = " %s -fPIC -fvisibility=hidden -fvisibility-inlines-hidden %s " % ( CFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " CFLAGS " ] = CFLAGS
LDFLAGS = " %s %s " % ( LDFLAGS , str . join ( " " , ADDITIONAL_ARGS ) )
os . environ [ " LDFLAGS " ] = LDFLAGS
# Some dependencies need a more recent CMake version than most distros provide
build_dependency ( name = " cmake- %s " % ( CMAKE_VERSION , ) , mode = " autoconf " , build_tool_args = [ ] , download_url = " https://cmake.org/files/v %s " % ( CMAKE_VERSION_2 , ) , download_name = " cmake- %s .tar.gz " % ( CMAKE_VERSION , ) )
# Extract compiler flags from CMake to harmonize settings with other autoconf dependencies
CMAKE_FLAG_EXTRACT_DIR = " ifcopenshell_cmake_test_ %s " % ( time . time ( ) , )
# was sp.check_output([bash, "-c", "cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 32"]), in bash script, unclear what the exact required format is and whether it's needed
if os . path . exists ( CMAKE_FLAG_EXTRACT_DIR ) :
shutil . rmtree ( CMAKE_FLAG_EXTRACT_DIR )
os . makedirs ( CMAKE_FLAG_EXTRACT_DIR )
BUILD_CFG_UPPER = BUILD_CFG . upper ( )
for FL in [ " C " , " CXX " ] :
__check_call__ ( [ bash , " -c " , """ echo "
message( \" \ $ { CMAKE_ %s _FLAGS_ %s } \" )
" > CMakeLists.txt """ % ( FL , BUILD_CFG_UPPER ) ] , cwd = CMAKE_FLAG_EXTRACT_DIR )
FL = " %s FLAGS " % ( FL , )
FLM = " %s FLAGS_MINIMAL " % ( FL , )
# @TODO: bash code unclear
# exec("%sFLAGS=%s" % (FL, sp.check_output([os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake"), "."
# declare ${FL}FLAGS_MINIMAL="`$DEPS_DIR/install/cmake-$CMAKE_VERSION/bin/cmake . 2>&1 >/dev/null` ${!FLM}"
shutil . rmtree ( CMAKE_FLAG_EXTRACT_DIR )
2017-01-19 10:24:09 +01:00
build_dependency ( name = " pcre- %s " % ( PCRE_VERSION , ) , mode = " autoconf " , build_tool_args = [ " --disable-shared " ] , download_url = " https://downloads.sourceforge.net/project/pcre/pcre/ %s / " % ( PCRE_VERSION , ) , download_name = " pcre- %s .tar.bz2 " % ( PCRE_VERSION , ) )
2016-09-10 13:09:50 +02:00
# An issue exists with swig-1.3 and python >= 3.2
# Therefore, build a recent copy from source
2017-10-30 10:26:27 +01:00
build_dependency ( name = " swig " , mode = " autoconf " , build_tool_args = [ " --with-pcre-prefix= %s /install/pcre- %s " % ( DEPS_DIR , PCRE_VERSION ) ] , download_url = " https://github.com/swig/swig.git " , download_name = " swig " , download_tool = download_tool_git , revision = " rel- %s " % SWIG_VERSION )
2016-09-10 13:09:50 +02:00
2017-08-13 13:13:26 +02:00
if USE_OCCT :
build_dependency (
name = " occt- %s " % OCCT_VERSION ,
mode = " cmake " ,
build_tool_args = [
" -DINSTALL_DIR= %s /install/occt- %s " % ( DEPS_DIR , OCCT_VERSION ) ,
" -DBUILD_LIBRARY_TYPE=Static " ,
" -DBUILD_MODULE_Draw=0 " ,
] ,
2018-08-19 10:29:01 +02:00
download_url = " https://git.dev.opencascade.org/repos/occt.git " ,
download_name = " occt " ,
download_tool = download_tool_git ,
2018-09-18 13:39:44 +02:00
patch = " ./patches/occt/enable-exception-handling.patch " ,
2018-08-19 10:29:01 +02:00
revision = " V %s " % OCCT_VERSION . replace ( ' . ' , ' _ ' ) )
2017-08-13 13:13:26 +02:00
else :
build_dependency ( name = " oce- %s " % ( OCE_VERSION , ) , mode = " cmake " , build_tool_args = [ " -DOCE_DISABLE_TKSERVICE_FONT=ON " , " -DOCE_TESTING=OFF " , " -DOCE_BUILD_SHARED_LIB=OFF " , " -DOCE_DISABLE_X11=ON " , " -DOCE_VISUALISATION=OFF " , " -DOCE_OCAF=OFF " , " -DOCE_INSTALL_PREFIX= %s /install/oce- %s " % ( DEPS_DIR , OCE_VERSION ) ] , download_url = " https://github.com/tpaviot/oce/archive/ " , download_name = " OCE- %s .tar.gz " % ( OCE_VERSION , ) )
2016-09-10 13:09:50 +02:00
build_dependency ( " libxml2- %s " % ( LIBXML_VERSION , ) , " autoconf " , build_tool_args = [ " --without-python " , " --disable-shared " , " --without-zlib " , " --without-iconv " , " --without-lzma " ] , download_url = " ftp://xmlsoft.org/libxml2/ " , download_name = " libxml2- %s .tar.gz " % ( LIBXML_VERSION , ) )
build_dependency ( " OpenCOLLADA " , " cmake " , build_tool_args = [ " -DLIBXML2_INCLUDE_DIR= %s /install/libxml2- %s /include/libxml2 " % ( DEPS_DIR , LIBXML_VERSION ) , " -DLIBXML2_LIBRARIES= %s /install/libxml2- %s /lib/libxml2.a " % ( DEPS_DIR , LIBXML_VERSION ) , " -DPCRE_INCLUDE_DIR= %s /install/pcre- %s /include " % ( DEPS_DIR , PCRE_VERSION ) , " -DPCRE_PCREPOSIX_LIBRARY= %s /install/pcre- %s /lib/libpcreposix.a " % ( DEPS_DIR , PCRE_VERSION ) , " -DPCRE_PCRE_LIBRARY= %s /install/pcre- %s /lib/libpcre.a " % ( DEPS_DIR , PCRE_VERSION ) , " -DCMAKE_INSTALL_PREFIX= %s /install/OpenCOLLADA/ " % ( DEPS_DIR , ) ] , download_url = " https://github.com/KhronosGroup/OpenCOLLADA.git " , download_name = " OpenCOLLADA " , download_tool = download_tool_git , revision = OPENCOLLADA_COMMIT )
# Python should not be built with -fvisibility=hidden, from experience that introduces segfaults
OLD_CXX_FLAGS = os . environ [ " CXXFLAGS " ]
OLD_C_FLAGS = os . environ [ " CFLAGS " ]
os . environ [ " CXXFLAGS " ] = CXXFLAGS_MINIMAL
os . environ [ " CFLAGS " ] = CFLAGS_MINIMAL
# On OSX a dynamic python library is built or it would not be compatible
# with the system python because of some threading initialization
PYTHON_CONFIGURE_ARGS = [ ]
if get_os ( ) == " Darwin " :
PYTHON_CONFIGURE_ARGS = [ " --disable-static " , " --enable-shared " ]
2017-07-03 20:42:18 +02:00
def get_python_unicode_confs ( py_ver ) :
if py_ver < " 3.3 " :
return [ ( " --enable-unicode=ucs2 " , " " ) , ( " --enable-unicode=ucs4 " , " u " ) ]
else : return [ ( " " , " " ) ]
def PYTHON_VERSION_CONFS ( ) :
for v in PYTHON_VERSIONS :
for unicode_conf , abi_tag in get_python_unicode_confs ( v ) :
yield v , unicode_conf , abi_tag
2016-09-10 13:09:50 +02:00
2017-07-03 20:42:18 +02:00
for PYTHON_VERSION , unicode_conf , abi_tag in PYTHON_VERSION_CONFS ( ) :
build_dependency ( " python- %s %s " % ( PYTHON_VERSION , abi_tag ) , " autoconf " , PYTHON_CONFIGURE_ARGS + [ unicode_conf ] , " http://www.python.org/ftp/python/ %s / " % ( PYTHON_VERSION , ) , " Python- %s .tgz " % ( PYTHON_VERSION , ) )
2016-09-10 13:09:50 +02:00
os . environ [ " CXXFLAGS " ] = OLD_CXX_FLAGS
os . environ [ " CFLAGS " ] = OLD_C_FLAGS
2017-11-18 16:50:38 +01:00
str_concat = lambda prefix : lambda postfix : " " if postfix . strip ( ) == " " else " = " . join ( ( prefix , postfix . strip ( ) ) )
2017-07-25 16:26:42 +02:00
build_dependency ( " boost- %s " % ( BOOST_VERSION , ) , mode = " bjam " , build_tool_args = [ " --stagedir= %s /install/boost- %s " % ( DEPS_DIR , BOOST_VERSION ) , " --with-system " , " --with-program_options " , " --with-regex " , " --with-thread " , " --with-date_time " , " --with-iostreams " , " link=static " ] + BOOST_ADDRESS_MODEL + list ( map ( str_concat ( " cxxflags " ) , CXXFLAGS . strip ( ) . split ( ' ' ) ) ) + list ( map ( str_concat ( " linkflags " ) , LDFLAGS . strip ( ) . split ( ' ' ) ) ) + [ " stage " , " -s " , " NO_BZIP2=1 " ] , download_url = " http://downloads.sourceforge.net/project/boost/boost/ %s / " % ( BOOST_VERSION , ) , download_name = " boost_ %s .tar.bz2 " % ( BOOST_VERSION_UNDERSCORE , ) )
2016-09-10 13:09:50 +02:00
build_dependency ( name = " icu- %s " % ( ICU_VERSION , ) , mode = " icu " , build_tool_args = [ " --enable-static " , " --disable-shared " ] , download_url = " http://download.icu-project.org/files/icu4c/ %s / " % ( ICU_VERSION , ) , download_name = " icu4c- %s -src.tgz " % ( ICU_VERSION_UNDERSCORE , ) )
cecho ( " Building IfcOpenShell: " , GREEN )
IFCOS_DIR = os . path . join ( DEPS_DIR , " build " , " ifcopenshell " )
if os . path . exists ( IFCOS_DIR ) :
shutil . rmtree ( IFCOS_DIR )
os . makedirs ( IFCOS_DIR )
executables_dir = os . path . join ( IFCOS_DIR , " executables " )
if not os . path . exists ( executables_dir ) :
os . makedirs ( executables_dir )
logger . info ( " \r Configuring executables... " )
2017-08-13 13:13:26 +02:00
if USE_OCCT :
occ_include_dir = " %s /install/occt- %s /include/opencascade " % ( DEPS_DIR , OCCT_VERSION )
occ_library_dir = " %s /install/occt- %s /lib " % ( DEPS_DIR , OCCT_VERSION )
else :
occ_include_dir = " %s /install/oce- %s /include/oce " % ( DEPS_DIR , OCE_VERSION )
occ_library_dir = " %s /install/oce- %s /lib " % ( DEPS_DIR , OCE_VERSION )
2016-11-13 20:27:22 +01:00
run_cmake ( " " , cmake_args = [
" -DBOOST_ROOT= " " %s /install/boost- %s " % ( DEPS_DIR , BOOST_VERSION ) ,
2017-08-13 13:13:26 +02:00
" -DOCC_INCLUDE_DIR= " + occ_include_dir ,
" -DOCC_LIBRARY_DIR= " + occ_library_dir ,
2016-11-13 20:27:22 +01:00
" -DOPENCOLLADA_INCLUDE_DIR= " " %s /install/OpenCOLLADA/include/opencollada " % ( DEPS_DIR , ) ,
" -DOPENCOLLADA_LIBRARY_DIR= " " %s /install/OpenCOLLADA/lib/opencollada " % ( DEPS_DIR , ) ,
" -DICU_INCLUDE_DIR= " " %s /install/icu- %s /include " % ( DEPS_DIR , ICU_VERSION ) ,
" -DICU_LIBRARY_DIR= " " %s /install/icu- %s /lib " % ( DEPS_DIR , ICU_VERSION ) ,
" -DPCRE_LIBRARY_DIR= " " %s /install/pcre- %s /lib " % ( DEPS_DIR , PCRE_VERSION ) ,
" -DBUILD_IFCPYTHON= " " OFF " ,
2017-07-25 16:26:42 +02:00
" -DUSE_MMAP= " " OFF " ,
2016-11-13 20:27:22 +01:00
" -DCMAKE_INSTALL_PREFIX= " " %s /install/ifcopenshell " % ( DEPS_DIR , ) ] , cmake_dir = CMAKE_DIR , cwd = executables_dir )
2016-09-10 13:09:50 +02:00
logger . info ( " \r Building executables... " )
2016-11-06 19:07:03 +01:00
__check_call__ ( [ make , " -j %s " % ( IFCOS_NUM_BUILD_PROCS , ) ] , cwd = executables_dir )
2018-09-12 13:57:41 +02:00
__check_call__ ( [ make , " install/strip " if BUILD_CFG == " Release " else " install " ] , cwd = executables_dir )
2016-09-10 13:09:50 +02:00
# On OSX the actual Python library is not linked against.
ADDITIONAL_ARGS = " "
if get_os ( ) == " Darwin " :
ADDITIONAL_ARGS = " -Wl,-flat_namespace,-undefined,suppress "
os . environ [ " CXXFLAGS " ] = " %s %s " % ( CXXFLAGS_MINIMAL , ADDITIONAL_ARGS )
os . environ [ " CFLAGS " ] = " %s %s " % ( CFLAGS_MINIMAL , ADDITIONAL_ARGS )
os . environ [ " LDFLAGS " ] = " %s %s " % ( LDFLAGS , ADDITIONAL_ARGS )
2017-07-03 20:42:18 +02:00
for PYTHON_VERSION , _ , TAG in PYTHON_VERSION_CONFS ( ) :
logger . info ( " \r Configuring python %s %s wrapper... " % ( PYTHON_VERSION , TAG ) )
2016-09-10 13:09:50 +02:00
2017-07-03 20:42:18 +02:00
python_dir = os . path . join ( IFCOS_DIR , " python- %s %s " % ( PYTHON_VERSION , TAG ) )
2016-09-10 13:09:50 +02:00
if not os . path . exists ( python_dir ) :
os . makedirs ( python_dir )
2017-07-03 20:42:18 +02:00
PYTHON_LIBRARY = __check_output__ ( [ bash , " -c " , " ls %s /install/python- %s %s /lib/libpython*.* " % ( DEPS_DIR , PYTHON_VERSION , TAG ) ] , cwd = None ) . strip ( )
PYTHON_INCLUDE = __check_output__ ( [ bash , " -c " , " ls -d %s /install/python- %s %s /include/python* " % ( DEPS_DIR , PYTHON_VERSION , TAG ) ] , cwd = None ) . strip ( )
PYTHON_EXECUTABLE = os . path . join ( DEPS_DIR , " install " , " python- %s %s " % ( PYTHON_VERSION , TAG ) , " bin " , " python %s " % ( PYTHON_VERSION [ 0 ] , ) )
2016-09-10 13:09:50 +02:00
os . environ [ " PYTHON_LIBRARY_BASENAME " ] = os . path . basename ( PYTHON_LIBRARY )
run_cmake ( " " , cmake_args = [ " -DBOOST_ROOT= %s /install/boost- %s " % ( DEPS_DIR , BOOST_VERSION ) ,
2017-08-13 13:13:26 +02:00
" -DOCC_INCLUDE_DIR= " + occ_include_dir ,
" -DOCC_LIBRARY_DIR= " + occ_library_dir ,
2016-09-10 13:09:50 +02:00
" -DOPENCOLLADA_INCLUDE_DIR= %s /install/OpenCOLLADA/include/opencollada " % ( DEPS_DIR , ) ,
" -DOPENCOLLADA_LIBRARY_DIR= %s /install/OpenCOLLADA/lib/opencollada " % ( DEPS_DIR , ) ,
" -DICU_INCLUDE_DIR= %s /install/icu- %s /include " % ( DEPS_DIR , ICU_VERSION ) ,
" -DICU_LIBRARY_DIR= %s /install/icu- %s /lib " % ( DEPS_DIR , ICU_VERSION ) ,
" -DPYTHON_LIBRARY= %s " % ( PYTHON_LIBRARY , ) ,
" -DPYTHON_EXECUTABLE= %s " % ( PYTHON_EXECUTABLE , ) ,
" -DPYTHON_INCLUDE_DIR= %s " % ( PYTHON_INCLUDE , ) ,
" -DSWIG_EXECUTABLE= %s /install/swig/bin/swig " % ( DEPS_DIR , ) ,
2016-11-13 20:27:22 +01:00
" -DCMAKE_INSTALL_PREFIX= %s /install/ifcopenshell/tmp " % ( DEPS_DIR , ) ,
2016-09-10 13:09:50 +02:00
" -DCOLLADA_SUPPORT=OFF " ] , cmake_dir = CMAKE_DIR , cwd = python_dir )
2017-07-03 20:42:18 +02:00
logger . info ( " \r Building python %s %s wrapper... " % ( PYTHON_VERSION , TAG ) )
2016-09-10 13:09:50 +02:00
2016-11-06 19:07:03 +01:00
__check_call__ ( [ make , " -j %s " % ( IFCOS_NUM_BUILD_PROCS , ) , " _ifcopenshell_wrapper " ] , cwd = python_dir )
2016-11-13 20:27:22 +01:00
__check_call__ ( [ make , " install/local " ] , cwd = os . path . join ( python_dir , " ifcwrap " ) )
module_dir = os . path . dirname ( __check_output__ ( [ PYTHON_EXECUTABLE , " -c " , " from __future__ import print_function; import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell)) " ] ) )
2016-09-10 13:09:50 +02:00
if get_os ( ) != " Darwin " :
# TODO: This symbol name depends on the Python version?
2016-11-13 20:27:22 +01:00
__check_call__ ( [ strip , " -s " , " -K " , " PyInit__ifcopenshell_wrapper " , " _ifcopenshell_wrapper.so " ] , cwd = module_dir )
2017-07-03 20:42:18 +02:00
__check_call__ ( [ cp , " -R " , module_dir , os . path . join ( DEPS_DIR , " install " , " ifcopenshell " , " python- %s %s " % ( PYTHON_VERSION , TAG ) ) ] )
2016-09-10 13:09:50 +02:00
logger . info ( " \r Built IfcOpenShell... \n \n " )