diff --git a/build.bat b/build.bat new file mode 100644 index 0000000000000000000000000000000000000000..b912f70ef0fbb4fe8c834f9df6f3c6ddea8af9d6 --- /dev/null +++ b/build.bat @@ -0,0 +1,58 @@ +pushd %~dp0 +SET "CURRENTDIR=%cd%" +SET "BUILDDIR=%CURRENTDIR%\build" +SET "BUILDTOOLSDIR=%BUILDDIR%\buildtools" +SET "VCVARSBAT=%BUILDTOOLSDIR%\VC\Auxiliary\Build\vcvars64.bat" +SET "SIPDIR=%BUILDDIR%\sip-4.19.3" +SET "SIPINSTALLDIR=%BUILDDIR%\sipinstall" +SET "vs_buildtoolsexe=%BUILDDIR%\vs_buildtools.exe" +SET "FBXSDKDIR=%BUILDDIR%\fbxsdk" +SET "FBXSDKPYTHONDIR=%BUILDDIR%\fbxsdkpy" +SET "FBXDIR=%CURRENTDIR%\fbx" +mkdir "%BUILDDIR%" +mkdir "%BUILDTOOLSDIR%" +mkdir "%SIPINSTALLDIR%" +mkdir "%FBXSDKDIR%" +mkdir "%FBXSDKPYTHONDIR%" +mkdir "%FBXDIR%" + +echo "Download and install Visual Studio Build Tools (nmake, MSVC, link, etc.)" + +curl -L -o %vs_buildtoolsexe% https://aka.ms/vs/16/release/vs_buildtools.exe + +%vs_buildtoolsexe% --quiet --wait --norestart --nocache --installPath "%BUILDTOOLSDIR%" --remove Microsoft.VisualStudio.Component.Windows10SDK.10240 --remove Mi +crosoft.VisualStudio.Component.Windows10SDK.10586 --remove Microsoft.VisualStudio.Component.Windows10SDK.14393 --remove Microsoft.VisualStudio.Component.Windows +81SDK --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK +|| IF "%ERRORLEVEL%"=="3010" EXIT 0 + + +echo "Load environment variables to use Build Tools from this script" +call %VCVARSBAT% + +for /F "tokens=1,2 delims=;" %%i in (reqs_win.txt) do ( + curl -L -o "%BUILDDIR%\%%i.exe" %%j + "%BUILDDIR%\%%i.exe" /S /D=%BUILDDIR%\%%i +) + +::curl -L -O "https://gitlab.inria.fr/radili/fbxsdk_python/uploads/12002ae82d20e4d6b60107dacb5abe4b/sip-4.19.3.tar.gz" + +tar xvf sip-4.19.3.tar.gz -C "%BUILDDIR%" +cd "%BUILDDIR%"\sip-* +python "configure.py" -b "%SIPINSTALLDIR%" -d "%SIPINSTALLDIR%" -e "%SIPINSTALLDIR%" --pyidir="%SIPINSTALLDIR%" +nmake +nmake install +cd %CURRENTDIR% + +copy "PythonBindings.py" "%FBXSDKPYTHONDIR%/PythonBindings.py" +SET "FBXSDK_ROOT=%FBXSDKDIR%" +SET "SIP_ROOT=%SIPDIR%" +python "%FBXSDKPYTHONDIR%"/PythonBindings.py Python3_x64 +copy "%FBXSDKPYTHONDIR%\build\Distrib\site-packages\fbx\*" "%FBXDIR%" +copy "%SIPINSTALLDIR%\sip.pyd" "%FBXDIR%" + +%vs_buildtoolsexe% uninstall --quiet --wait --norestart --nocache --installPath "%BUILDTOOLSDIR%" --remove Microsoft.VisualStudio.Component.Windows10SDK.10240 - +-remove Microsoft.VisualStudio.Component.Windows10SDK.10586 --remove Microsoft.VisualStudio.Component.Windows10SDK.14393 --remove Microsoft.VisualStudio.Compone +nt.Windows81SDK --remove Microsoft.VisualStudio.Workload.VCTools --remove Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --remove Microsoft.VisualStudio.Comp +onent.Windows10SDK || IF "%ERRORLEVEL%"=="3010" EXIT 0 +popd +pause \ No newline at end of file diff --git a/builder.py b/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..927dad376cb3320eb1ccb2fd1787523d26b4b5a3 --- /dev/null +++ b/builder.py @@ -0,0 +1,171 @@ +# Copyright (c) 2020, Riverbank Computing Limited +# All rights reserved. +# +# This copy of SIP is licensed for use under the terms of the SIP License +# Agreement. See the file LICENSE for more details. +# +# This copy of SIP may also used under the terms of the GNU General Public +# License v2 or v3 as published by the Free Software Foundation which can be +# found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +from distutils.command.build_ext import build_ext +from distutils.dist import Distribution +from distutils.extension import Extension +from distutils.log import ERROR, INFO, set_threshold + +import os +import sys + +from .buildable import BuildableModule +from .builder import Builder +from .exceptions import UserException +from .installable import Installable + + +class DistutilsBuilder(Builder): + """ The implementation of a distutils-based project builder. """ + + def build_executable(self, buildable, *, fatal=True): + """ Build an executable from a BuildableExecutable object and return + the relative pathname of the executable. + """ + + raise UserException("DistutilsBuilder cannot build executables") + + def build_project(self, target_dir, *, wheel_tag=None): + """ Build the project. """ + + for buildable in self.project.buildables: + if isinstance(buildable, BuildableModule): + if buildable.static: + raise UserException( + "DistutilsBuilder cannot build static modules") + + self._build_extension_module(buildable) + else: + raise UserException( + "DistutilsBuilder cannot build '{0}' buildables".format( + type(buildable).__name__)) + + def install_project(self, target_dir, *, wheel_tag=None): + """ Install the project into a target directory. """ + + project = self.project + + installed = [] + + # Install any project-level installables. + for installable in project.installables: + installable.install(target_dir, installed) + + # Install any installables from built buildables. + for buildable in project.buildables: + for installable in buildable.installables: + installable.install(target_dir, installed) + + if project.distinfo: + from .distinfo import create_distinfo + + create_distinfo(project.get_distinfo_dir(target_dir), wheel_tag, + installed, project.metadata, project.get_requires_dists(), + project.root_dir, project.console_scripts, + project.gui_scripts) + + def _build_extension_module(self, buildable): + """ Build an extension module from the sources. """ + + project = self.project + + set_threshold(INFO if project.verbose else ERROR) + + distribution = Distribution() + + module_builder = ExtensionCommand(distribution, buildable) + module_builder.build_lib = buildable.build_dir + module_builder.debug = buildable.debug + + if buildable.debug: + # Enable assert(). + module_builder.undef = 'NDEBUG' + + module_builder.ensure_finalized() + + # Convert the #defines. + define_macros = [] + for macro in buildable.define_macros: + parts = macro.split('=', maxsplit=1) + name = parts[0] + try: + value = parts[1] + except IndexError: + value = None + + define_macros.append((name, value)) + + buildable.make_names_relative() + + if sys.platform == "linux": + linux_static_libs = [ os.path.abspath(a) for a in project.linux_static_libraries ] + else: + linux_static_libs = [] + + + module_builder.extensions = [ + Extension(buildable.fq_name, buildable.sources, + define_macros=define_macros, + include_dirs=buildable.include_dirs, + libraries=buildable.libraries, + library_dirs=buildable.library_dirs, + extra_link_args=linux_static_libs)] + + project.progress( + "Compiling the '{0}' module".format(buildable.fq_name)) + + saved_cwd = os.getcwd() + os.chdir(buildable.build_dir) + + try: + module_builder.run() + except Exception as e: + raise UserException( + "Unable to compile the '{0}' module".format( + buildable.fq_name), + detail=str(e)) + + # Add the extension module to the buildable's list of installables. + installable = Installable('module', + target_subdir=buildable.get_install_subdir()) + installable.files.append( + module_builder.get_ext_fullpath(buildable.fq_name)) + buildable.installables.append(installable) + + os.chdir(saved_cwd) + + +class ExtensionCommand(build_ext): + """ Extend the distutils command to build an extension module. """ + + def __init__(self, distribution, buildable): + """ Initialise the object. """ + + super().__init__(distribution) + + self._buildable = buildable + + def get_ext_filename(self, ext_name): + """ Reimplemented to handle modules that use the limited API. """ + + return os.path.join(*ext_name.split('.')) + self._buildable.get_module_extension() diff --git a/create_sdist.sh b/create_sdist.sh index 806d5ef6d072e1c4e748f874482bf1c3b9c1384c..0e2bee59e6e68aa9aad20899d8b03d4aae6d0bf3 100755 --- a/create_sdist.sh +++ b/create_sdist.sh @@ -17,15 +17,16 @@ reqsfile="${scdir}"/reqs.txt if [ ! -d "$fbxsdkdir" ] then mkdir "$fbxsdkdir" - url=$(grep 'fbx.*fbxsdk' $reqsfile) + url=$(grep 'fbx.*fbxsdk' $reqsfile|cut -d';' -f 2) file=${url##*/} tar="${fbxsdkdir}/${file}" curl -L -o "$tar" $url tar -xvzf "$tar" -C "$fbxsdkdir" - printf "yes\nn\n" |"$fbxsdkdir"/fbx*fbxsdk_linux "$fbxsdkdir" + printf "yes\nn\n" |"$fbxsdkdir"/fbx*fbxsdk_linux "$fbxsdkdir" >/dev/null 2>&1 # patch libfbxsdk.so because it is not linked against libxml2 and libz for some reason patchelf --add-needed libz.so.1 ${fbxsdkdir}/lib/gcc/x64/release/libfbxsdk.so patchelf --add-needed libxml2.so.2 ${fbxsdkdir}/lib/gcc/x64/release/libfbxsdk.so + mv "${fbxsdkdir}/lib/gcc" "${fbxsdkdir}/lib/all" else echo "Skipping installation of fbxsdk because "$fbxsdkdir" exists" fi @@ -34,18 +35,16 @@ fi if [ ! -d "$fbxpydir" ] then mkdir "$fbxpydir" - url=$(grep 'fbx.*fbxpythonbindings_linux' $reqsfile) + url=$(grep 'fbx.*fbxpythonbindings_linux' $reqsfile|cut -d';' -f 2) file=${url##*/} tar="${fbxpydir}/${file}" curl -L -o "$tar" $url tar -xvzf "$tar" -C "$fbxpydir" - printf "yes\nn\n"|"$fbxpydir"/fbx*fbxpythonbindings_linux "$fbxpydir" + printf "yes\nn\n"|"$fbxpydir"/fbx*fbxpythonbindings_linux "$fbxpydir" >/dev/null 2>&1 + # patch sip files and library headers so that they compile with sip5 patch -N -p0 < patch + else echo "Skipping installation of fbx python bindings because "$fbxpydir" exists" fi - -sip-wheel --verbose - -LD_LIBRARY_PATH=${fbxsdkdir}/lib/gcc/x64/release/:$LD_LIBRARY_PATH auditwheel -v repair $(ls -1t fbx*.whl|head -1) diff --git a/project.py b/project.py index fbadd3dfe99c8451f3144872cafacd710a0309b0..3de3e1177ffbf3104bcb8ee349e7c2e7978dc7f4 100644 --- a/project.py +++ b/project.py @@ -1,5 +1,6 @@ import os import sys +# import ipdb from sipbuild import Option, Project @@ -20,7 +21,7 @@ class FBXSDKPyProject(Project): help="a list of libraries needed on the windows platform for Python >=3.7", option_type=list, metavar="LIST") options.append(win_libs_option) - win_py36_libs_option = Option('win36_libraries', + win_py36_libs_option = Option('win_py36_libraries', help="a list of libraries needed on the windows platform for Python <=3.6", option_type=list, metavar="LIST") options.append(win_py36_libs_option) @@ -28,6 +29,10 @@ class FBXSDKPyProject(Project): help="a list of libraries needed on the linuxdows platform", option_type=list, metavar="LIST") options.append(linux_libs_option) + linux_static_libs_option = Option('linux_static_libraries', + help="a list of libraries needed on the linuxdows platform", option_type=list, metavar="LIST") + options.append(linux_static_libs_option) + return options def update(self, tool): @@ -39,14 +44,23 @@ class FBXSDKPyProject(Project): libraries = [] if sys.platform == "win32": if sys.version_info.major == 3 and sys.version_info.minor < 7: - libraries = self.win_libs_option + libraries = self.win_libraries else: - libraries = self.win_py36_libs_option + libraries = self.win_py36_libraries elif sys.platform == "linux": - libraries = self.linux_libs_option + libraries = self.linux_libraries else: raise Exception("Your platform "+sys.platform+" is not supported") fbx_bindings.libraries.extend(libraries) + def setup(self, pyproject, tool, tool_description): + self.verbose = True + if sys.platform == "win32": + super().run_command(["call create_sdist.bat"],fatal=True) + elif sys.platform == "linux": + super().run_command(["./create_sdist.sh"],fatal=True) + else: + raise Exception("Your platform "+sys.platform+" is not supported") + super().setup(pyproject, tool, tool_description) diff --git a/pyproject.toml b/pyproject.toml index 8fd0f42fe566af50c6b942a2fceb2ca04289edb6..3e12cf889ef26e21d6324c43b02ad7c7f1313620 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,13 +12,13 @@ version = "2020.1" [tool.sip.bindings.fbx_module] headers = ["fbxsdk.h"] include-dirs = ["build/fbxsdk/include"] -libraries = ["fbxsdk"] library-dirs = ["build/fbxsdk/lib/all/x64/release"] -win-libraries = ["libfbxsdk-md", "zlib-md", "libxml2-md", "Advapi32", "Wininet"] -win-py36-libraries = ["libfbxsdk-mt", "zlib-mt", "libxml2-mt", "Advapi32", "Wininet"] -linux-libraries = ["build/fbxsdk/lib/all/x64/release"] [tool.sip.project] sip-files-dir = "build/fbxpy/sip" wheel-includes = ["build/fbxpy/common/FbxCommon.py"] minimum-glibc-version = "2.17" +linux-libraries = ["xml2", "z"] +linux-static-libraries = ["build/fbxsdk/lib/all/x64/release/libfbxsdk.a"] +win-libraries = ["libfbxsdk-md", "zlib-md", "libxml2-md", "Advapi32", "Wininet"] +win-py36-libraries = ["libfbxsdk-mt", "zlib-mt", "libxml2-mt", "Advapi32", "Wininet"] diff --git a/reqs.txt b/reqs.txt index 9f1fcef61c48caa0fd909a78375c2e3a5416da18..3acb880cb0875d41a75e73be34585f9b4cf5ade2 100644 --- a/reqs.txt +++ b/reqs.txt @@ -1,4 +1,2 @@ fbxsdk;https://www.autodesk.com/content/dam/autodesk/www/adn/fbx/2020-1-1/fbx202011_fbxsdk_linux.tar.gz fbxpy;https://www.autodesk.com/content/dam/autodesk/www/adn/fbx/2020-1-1/fbx202011_fbxpythonbindings_linux.tar.gz -fbxsdk;https://www.autodesk.com/content/dam/autodesk/www/adn/fbx/2020-1/fbx20201_fbxsdk_vs2017_win.exe -fbxpy;https://www.autodesk.com/content/dam/autodesk/www/adn/fbx/2020-1/fbx20201_fbxpythonbindings_win.exe diff --git a/reqs_win.txt b/reqs_win.txt new file mode 100644 index 0000000000000000000000000000000000000000..52616984d8855eb04e8de08d92d17849cb0d14e9 --- /dev/null +++ b/reqs_win.txt @@ -0,0 +1,2 @@ +fbxsdk;https://www.autodesk.com/content/dam/autodesk/www/adn/fbx/2020-1/fbx20201_fbxsdk_vs2017_win.exe +fbxpy;https://www.autodesk.com/content/dam/autodesk/www/adn/fbx/2020-1/fbx20201_fbxpythonbindings_win.exe