#!/usr/bin/env python3 import glob, os, platform, re, shutil, sys, tempfile assert sys.version_info >= ( 3, 8 ) from Detail.Config import config import Detail.Utility as utility class Build: platformString = "" productVersion = "x.y.z" productStage = "stage" productBaseName = "Synergy" productPackageName = "" def __init__( self ): self.configure() def __configureSubmodules( self ): utility.printHeading( "Updating Git submodules..." ) os.chdir( config.toplevelPath() ) utility.runCommand( "git submodule update --init --remote --recursive" ) def __configureCMake( self ): utility.printHeading( "Configuring CMake..." ) command = ( "cmake" ' -S "' + config.synergyCorePath() + '" ' ' -B "' + config.synergyBuildPath() + '" ' " -D CMAKE_BUILD_TYPE=Release " " -D CMAKE_CONFIGURATION_TYPES=Release " " -D SYNERGY_ENTERPRISE=ON " ) if platform.system() == "Darwin": command += " -D CMAKE_OSX_DEPLOYMENT_TARGET=10.12 " command += " -D CMAKE_OSX_ARCHITECTURES=x86_64 " if config.libQtPath() != "": command += ' -D CMAKE_PREFIX_PATH="' + config.libQtPath() +'" ' if config.cmakeGenerator() != "": command += ' -G "' + config.cmakeGenerator() + '" ' if config.vcvarsallPath() != "": command = 'call "' + config.vcvarsallPath() + '" x64 && ' + command utility.runCommand( command ) def __configureVersion( self ): utility.printHeading( "Configuring version information..." ) def makePlatformString(): if platform.system() == "Windows": return "-".join( [ platform.system(), platform.release(), platform.machine() ] ) import distro # TODO: Move this to global scope when distro supports Windows platformInfo = list( distro.linux_distribution( full_distribution_name = False ) ); while "" in platformInfo: platformInfo.remove( "" ) platformInfo.append( platform.machine() ) return "-".join( platformInfo ) def makeProductVersion(): versionFile = open( config.synergyVersionPath(), "r" ) versionData = versionFile.read(); versionFile.close() versionParts = re.findall( r" *(?:SET|export) +SYNERGY_VERSION_\w+ *= *\"?(\w+)\"?", versionData ) if len( versionParts ) != 4: printError( "Failed to extract version information." ) raise SystemExit( 1 ) return versionParts self.platformString = makePlatformString() versionParts = makeProductVersion() self.productVersion = ".".join( versionParts[ 0:3 ] ) self.productStage = versionParts[ 3 ] self.productPackageName = "-".join( [ self.productBaseName, self.productVersion, self.productStage, self.platformString ] ).lower() utility.printItem( "platformString: ", self.platformString ) utility.printItem( "productVersion: ", self.productVersion ) utility.printItem( "productStage: ", self.productStage ) utility.printItem( "productPackageName: ", self.productPackageName ) def configure( self ): self.__configureSubmodules() self.__configureCMake() self.__configureVersion() # Windows builds def __windowsMakeBinaries( self ): utility.printHeading( "Building binaries..." ) os.chdir( config.synergyBuildPath() ) utility.runCommand( 'call "' + config.vcvarsallPath() + '" x64 && ' 'msbuild synergy-core.sln /p:Platform="x64" /p:Configuration=Release /m ') def __windowsMakeMSI( self ): utility.printHeading( "Building MSI package..." ) installerPath = utility.joinPath( config.synergyBuildPath(), "installer" ) os.chdir( installerPath ) utility.runCommand( 'call "' + config.vcvarsallPath() + '" x64 && ' "msbuild Synergy.sln /p:Configuration=Release " ) sourcePath = utility.joinPath( installerPath, "bin/Release/Synergy.msi" ) targetPath = utility.joinPath( config.binariesPath(), self.productPackageName + ".msi" ) shutil.move( sourcePath, targetPath ) def __windowsMakeZIP( self ): utility.printHeading( "Building standalone ZIP package..." ) def copySynergyBinaries( sourcePath, productPath ): fileList = [ "libEGL.dll", "libGLESv2.dll", "Qt5Core.dll", "Qt5Gui.dll", "Qt5Network.dll", "Qt5Svg.dll", "Qt5Widgets.dll", "synergy.exe", "synergyc.exe", "synergyd.exe", "synergys.exe", "syntool.exe" ] if not os.path.exists( productPath ): os.mkdir( productPath ) for fileName in fileList: filePath = utility.joinPath( sourcePath, fileName ) shutil.copy2( filePath, productPath ) shutil.copytree( utility.joinPath( sourcePath, "Platforms" ), utility.joinPath( productPath, "Platforms" ), dirs_exist_ok = True ) shutil.copytree( utility.joinPath( sourcePath, "Styles" ), utility.joinPath( productPath, "Styles" ), dirs_exist_ok = True ) def copyOpenSSLBinaries( sourceOpenSSLPath, productPath ): productOpenSSLPath = utility.joinPath( productPath, "OpenSSL" ) if not os.path.exists( productOpenSSLPath ): os.mkdir( productOpenSSLPath ) for filePath in glob.glob( sourceOpenSSLPath + "/*" ): shutil.copy2( filePath, productOpenSSLPath ) for filePath in glob.glob( sourceOpenSSLPath + "/*.dll" ): shutil.copy2( filePath, productPath ) def makeZipArchive( productPath, zipPath ): rootPath = utility.joinPath( productPath, ".." ) basePath = os.path.relpath( productPath, rootPath ) shutil.make_archive( zipPath, 'zip', rootPath, basePath ) sourcePath = utility.joinPath( config.synergyBuildPath(), "bin/Release" ) productPath = utility.joinPath( config.synergyBuildPath(), "bin", self.productPackageName ) copySynergyBinaries( sourcePath, productPath ) sourceOpenSSLPath = utility.joinPath( config.synergyCorePath(), "ext/openssl/windows/x64/bin" ) copyOpenSSLBinaries( sourceOpenSSLPath, productPath ) zipPath = utility.joinPath( config.binariesPath(), self.productPackageName ) makeZipArchive( productPath, zipPath ) # Darwin builds def __darwinMakeApplication( self ): utility.printHeading( "Building application bundle..." ) os.chdir( config.synergyBuildPath() ) # utility.runCommand( "make -j && make install/strip" ) appBundlePath = utility.joinPath( config.synergyBuildPath(), "bundle/Synergy.app" ) # utility.runCommand( 'macdeployqt "' + appBundlePath + '"' ) shutil.copytree( appBundlePath, utility.joinPath( config.binariesPath(), self.productBaseName + ".app" ), dirs_exist_ok = True ) def __darwinMakeDMG( self ): utility.printHeading( "Building DMG disk image file..." ) targetSymlinkPath = utility.joinPath( config.synergyBuildPath(), "bundle/Applications" ) if not os.path.exists( targetSymlinkPath ): os.symlink( "/Applications", targetSymlinkPath, target_is_directory = True ) volumeLabel = self.productBaseName + ' ' + self.productVersion; bundlePath = utility.joinPath( config.synergyBuildPath(), "bundle" ) packagePath = utility.joinPath( config.binariesPath(), self.productPackageName + ".dmg" ) utility.runCommand( "hdiutil create " '-volname "' + volumeLabel + '" ' '-srcfolder "' + bundlePath + '" ' '-ov -format UDZO ' + packagePath ) # Linux builds def __linuxMakeBinaries( self ): utility.printHeading( "Building binaries..." ) utility.runCommand( 'cmake --build "' + config.synergyBuildPath() + '" --parallel' ) def __linuxMakeAppImage( self ): utility.printHeading( "Building AppImage package..." ) os.chdir( config.toolsPath() ) utility.runCommand( "wget -O linuxdeploy -c " + config.linuxdeployURL() + " && chmod a+x linuxdeploy" ) appImagePath = utility.joinPath( config.synergyBuildPath(), self.productPackageName + ".AppDir" ) # Needed by linuxdeploy os.environ[ "VERSION" ] = "-".join( [ self.productVersion, self.productStage ] ).lower() utility.runCommand( "./linuxdeploy " '--appdir "' + appImagePath + '" ' '--executable "' + utility.joinPath( config.synergyBuildPath(), "bin/synergy" ) + '" ' '--executable "' + utility.joinPath( config.synergyBuildPath(), "bin/synergyc" ) + '" ' '--executable "' + utility.joinPath( config.synergyBuildPath(), "bin/synergyd" ) + '" ' '--executable "' + utility.joinPath( config.synergyBuildPath(), "bin/synergys" ) + '" ' '--executable "' + utility.joinPath( config.synergyBuildPath(), "bin/syntool" ) + '" ' '--create-desktop-file ' '--icon-file "' + utility.joinPath( config.synergyCorePath(), "res/synergy.svg" ) + '" ' '--output appimage' ) os.unsetenv( "VERSION" ) for fileName in glob.glob( "*.AppImage" ): shutil.move( utility.joinPath( config.toolsPath(), fileName ), utility.joinPath( config.binariesPath(), self.productPackageName + ".AppImage" ) ) def __linuxMakeDebian( self ): utility.printHeading( "Building Debian package..." ) def makeChangeLog(): if not os.path.exists( "./debian/changelog" ): os.environ[ "SYNERGY_ENTERPRISE" ] = "1" utility.runCommand( 'dch ' '--create ' '--package "' + self.productBaseName.lower() + '" ' '--controlmaint ' '--distribution unstable ' '--newversion ' + self.productVersion.lower() + ' ' '"Snapshot release."' ) def buildDebianPackage(): os.environ[ "DEB_BUILD_OPTIONS" ] = "parallel=8" utility.runCommand( "debuild --preserve-envvar SYNERGY_* -us -uc" ) def moveDebianPackage(): for fileName in glob.glob( "../" + self.productBaseName.lower() + "*.deb" ): shutil.move( utility.joinPath( config.synergyCorePath(), fileName ), utility.joinPath( config.binariesPath(), self.productPackageName + ".deb" ) ) for fileName in glob.glob( "../" + self.productBaseName.lower() + "*" ): shutil.move( utility.joinPath( config.synergyCorePath(), fileName ), config.synergyBuildPath() ) os.chdir( config.synergyCorePath() ) makeChangeLog() buildDebianPackage() moveDebianPackage() os.unsetenv( "SYNERGY_ENTERPRISE" ) os.unsetenv( "DEB_BUILD_OPTIONS" ) def __linuxMakeRPM( self ): utility.printHeading( "Building RPM package..." ) def makeSymlinkPath( temporaryPath ): sourceSymlinkPath = utility.joinPath( config.synergyBuildPath(), "rpm" ) targetSymlinkPath = utility.joinPath( temporaryPath, "rpm" ) os.symlink( sourceSymlinkPath, targetSymlinkPath, target_is_directory = True ) return targetSymlinkPath def installBinaries( installPath ): utility.runCommand( "cmake" ' -S "' + config.synergyCorePath() + '" ' ' -B "' + config.synergyBuildPath() + '" ' " -D CMAKE_BUILD_TYPE=Release " " -D SYNERGY_ENTERPRISE=ON " ' -D CMAKE_INSTALL_PREFIX:PATH="' + installPath + '" ' ) os.chdir( config.synergyBuildPath() ) utility.runCommand( "make -j && make install/strip" ) def makeRpmPackage( rpmToplevelPath, rpmBuildrootPath ): if " " in rpmToplevelPath: printError( "RPM top-level path contained spaces:\n\t", rpmToplevelPath ) raise SystemExit( 1 ) os.chdir( rpmToplevelPath ) utility.runCommand( 'rpmbuild -bb ' '--define "_topdir ' + rpmToplevelPath + '" ' '--buildroot ' + rpmBuildrootPath + ' ' "synergy.spec" ) def moveRpmPackage( rpmToplevelPath ): os.chdir( rpmToplevelPath ) for fileName in glob.glob( "RPMS/x86_64/*.rpm" ): shutil.move( fileName, utility.joinPath( config.binariesPath(), self.productPackageName + ".rpm" ) ) # rpmbuild can't handle spaces in paths so we operate inside a temporary path with tempfile.TemporaryDirectory() as temporaryPath: utility.printInfo( "Created: ", temporaryPath ) rpmToplevelPath = makeSymlinkPath( temporaryPath ) rpmBuildrootPath = utility.joinPath( rpmToplevelPath, "BUILDROOT" ) installPath = utility.joinPath( rpmBuildrootPath, "usr" ) installBinaries( installPath ) makeRpmPackage( rpmToplevelPath, rpmBuildrootPath ) moveRpmPackage( rpmToplevelPath ) def make( self ): if platform.system() == "Windows": self.__windowsMakeBinaries() self.__windowsMakeMSI() self.__windowsMakeZIP() elif platform.system() == "Darwin": self.__darwinMakeApplication() self.__darwinMakeDMG() elif platform.system() == "Linux": self.__linuxMakeBinaries() self.__linuxMakeAppImage() self.__linuxMakeDebian() self.__linuxMakeRPM() build = Build() build.make()