Using WebcamoidDeployTools.
This commit is contained in:
parent
e5097d3911
commit
14265e1220
19 changed files with 44 additions and 2382 deletions
|
@ -18,5 +18,10 @@
|
|||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
cd ports/deploy
|
||||
git clone https://github.com/webcamoid/DeployTools.git
|
||||
cd ../..
|
||||
|
||||
export PATH=/mingw64/bin:$PATH
|
||||
export PYTHONPATH="${PWD}/ports/deploy/DeployTools"
|
||||
python3 ports/deploy/deploy.py
|
||||
|
|
|
@ -22,6 +22,10 @@ if [ "${TRAVIS_OS_NAME}" = linux ]; then
|
|||
EXEC='sudo ./root.x86_64/bin/arch-chroot root.x86_64'
|
||||
fi
|
||||
|
||||
cd ports/deploy
|
||||
git clone https://github.com/webcamoid/DeployTools.git
|
||||
cd ../..
|
||||
|
||||
DEPLOYSCRIPT=deployscript.sh
|
||||
|
||||
if [ "${TRAVIS_OS_NAME}" = linux ]; then
|
||||
|
@ -34,6 +38,7 @@ if [ "${TRAVIS_OS_NAME}" = linux ]; then
|
|||
export LC_ALL=C
|
||||
export HOME=$HOME
|
||||
export PATH="$TRAVIS_BUILD_DIR/.local/bin:\$PATH"
|
||||
export PYTHONPATH="\$PWD/ports/deploy/DeployTools"
|
||||
export WINEPREFIX=/opt/.wine
|
||||
cd $TRAVIS_BUILD_DIR
|
||||
EOF
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import tools.utils
|
||||
from WebcamoidDeployTools import DTUtils
|
||||
|
||||
|
||||
if __name__ =='__main__':
|
||||
system = tools.utils.DeployToolsUtils().system
|
||||
system = DTUtils.Utils().system
|
||||
|
||||
while True:
|
||||
try:
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import shutil
|
||||
|
||||
import tools.utils
|
||||
|
||||
class DeployBase(tools.utils.DeployToolsUtils):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rootDir = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../..'))
|
||||
self.buildDir = os.environ['BUILD_PATH'] if 'BUILD_PATH' in os.environ else self.rootDir
|
||||
self.installDir = os.path.join(self.rootDir, 'ports/deploy/temp_priv/root')
|
||||
self.rootInstallDir = ''
|
||||
self.pkgsDir = os.path.join(self.rootDir,
|
||||
'ports/deploy/packages_auto',
|
||||
sys.platform if os.name == 'posix' else os.name)
|
||||
self.programVersion = ''
|
||||
self.qmake = ''
|
||||
|
||||
def __str__(self):
|
||||
deployInfo = 'Python version: {}\n' \
|
||||
'Root directory: {}\n' \
|
||||
'Build directory: {}\n' \
|
||||
'Install directory: {}\n' \
|
||||
'Packages directory: {}\n' \
|
||||
'System: {}\n' \
|
||||
'Architecture: {}\n' \
|
||||
'Target system: {}\n' \
|
||||
'Target architecture: {}\n' \
|
||||
'Number of threads: {}\n' \
|
||||
'Program version: {}\n' \
|
||||
'Make executable: {}\n' \
|
||||
'Qmake executable: {}'. \
|
||||
format(platform.python_version(),
|
||||
self.rootDir,
|
||||
self.buildDir,
|
||||
self.installDir,
|
||||
self.pkgsDir,
|
||||
self.system,
|
||||
self.arch,
|
||||
self.targetSystem,
|
||||
self.targetArch,
|
||||
self.njobs,
|
||||
self.programVersion,
|
||||
self.make,
|
||||
self.qmake)
|
||||
|
||||
return deployInfo
|
||||
|
||||
def run(self):
|
||||
print('Deploy info\n')
|
||||
print(self)
|
||||
print('\nPreparing for software packaging\n')
|
||||
self.prepare()
|
||||
|
||||
if not 'NO_SHOW_PKG_DATA_INFO' in os.environ \
|
||||
or os.environ['NO_SHOW_PKG_DATA_INFO'] != '1':
|
||||
print('\nPackaged data info\n')
|
||||
self.printPackageDataInfo()
|
||||
|
||||
if 'PACKAGES_PREPARE_ONLY' in os.environ \
|
||||
and os.environ['PACKAGES_PREPARE_ONLY'] == '1':
|
||||
print('\nPackage data is ready for merging\n')
|
||||
else:
|
||||
print('\nCreating packages\n')
|
||||
self.package()
|
||||
print('\nCleaning up')
|
||||
self.cleanup()
|
||||
print('Deploy finnished\n')
|
||||
|
||||
def printPackageDataInfo(self):
|
||||
packagedFiles = []
|
||||
|
||||
for root, _, files in os.walk(self.rootInstallDir):
|
||||
for f in files:
|
||||
packagedFiles.append(os.path.join(root, f))
|
||||
|
||||
packagedFiles = sorted(packagedFiles)
|
||||
|
||||
for f in packagedFiles:
|
||||
print(' ' + f)
|
||||
|
||||
def prepare(self):
|
||||
pass
|
||||
|
||||
def package(self):
|
||||
pass
|
||||
|
||||
def cleanup(self):
|
||||
shutil.rmtree(self.installDir, True)
|
|
@ -28,14 +28,16 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
|
||||
import deploy_base
|
||||
import tools.binary_mach
|
||||
import tools.qt5
|
||||
from WebcamoidDeployTools import DTDeployBase
|
||||
from WebcamoidDeployTools import DTQt5
|
||||
from WebcamoidDeployTools import DTBinaryMach
|
||||
|
||||
|
||||
class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
||||
class Deploy(DTDeployBase.DeployBase, DTQt5.Qt5Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
rootDir = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../..'))
|
||||
self.setRootDir(rootDir)
|
||||
self.installDir = os.path.join(self.buildDir, 'ports/deploy/temp_priv')
|
||||
self.pkgsDir = os.path.join(self.buildDir, 'ports/deploy/packages_auto', self.targetSystem)
|
||||
self.detectQt(os.path.join(self.buildDir, 'Manager'))
|
||||
|
@ -48,8 +50,8 @@ class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
|||
self.mainBinary = os.path.join(self.binaryInstallDir, self.programName)
|
||||
self.programVersion = self.detectVersion(os.path.join(self.rootDir, 'commons.pri'))
|
||||
self.detectMake()
|
||||
self.binarySolver = tools.binary_mach.DeployToolsBinary()
|
||||
self.binarySolver.readExcludeList(os.path.join(self.rootDir, 'ports/deploy/tools/exclude/exclude.{}.{}.txt'.format(os.name, sys.platform)))
|
||||
self.binarySolver = DTBinaryMach.MachBinaryTools()
|
||||
self.binarySolver.readExcludes(os.name, sys.platform)
|
||||
self.packageConfig = os.path.join(self.rootDir, 'ports/deploy/package_info.conf')
|
||||
self.dependencies = []
|
||||
self.installerConfig = os.path.join(self.installDir, 'installer/config')
|
||||
|
|
|
@ -28,14 +28,16 @@ import sys
|
|||
import tarfile
|
||||
import threading
|
||||
|
||||
import deploy_base
|
||||
import tools.binary_elf
|
||||
import tools.qt5
|
||||
from WebcamoidDeployTools import DTDeployBase
|
||||
from WebcamoidDeployTools import DTQt5
|
||||
from WebcamoidDeployTools import DTBinaryElf
|
||||
|
||||
|
||||
class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
||||
class Deploy(DTDeployBase.DeployBase, DTQt5.Qt5Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
rootDir = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../..'))
|
||||
self.setRootDir(rootDir)
|
||||
self.installDir = os.path.join(self.buildDir, 'ports/deploy/temp_priv')
|
||||
self.pkgsDir = os.path.join(self.buildDir, 'ports/deploy/packages_auto', sys.platform)
|
||||
self.detectQt(os.path.join(self.buildDir, 'Manager'))
|
||||
|
@ -52,8 +54,8 @@ class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
|||
elif 'android' in xspec:
|
||||
self.targetSystem = 'android'
|
||||
|
||||
self.binarySolver = tools.binary_elf.DeployToolsBinary()
|
||||
self.binarySolver.readExcludeList(os.path.join(self.rootDir, 'ports/deploy/tools/exclude/exclude.{}.{}.txt'.format(os.name, sys.platform)))
|
||||
self.binarySolver = DTBinaryElf.ElfBinaryTools()
|
||||
self.binarySolver.readExcludes(os.name, sys.platform)
|
||||
self.packageConfig = os.path.join(self.rootDir, 'ports/deploy/package_info.conf')
|
||||
self.dependencies = []
|
||||
self.installerConfig = os.path.join(self.installDir, 'installer/config')
|
||||
|
|
|
@ -26,14 +26,16 @@ import sys
|
|||
import threading
|
||||
import zipfile
|
||||
|
||||
import deploy_base
|
||||
import tools.binary_pecoff
|
||||
import tools.qt5
|
||||
from WebcamoidDeployTools import DTDeployBase
|
||||
from WebcamoidDeployTools import DTQt5
|
||||
from WebcamoidDeployTools import DTBinaryPecoff
|
||||
|
||||
|
||||
class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
||||
class Deploy(DTDeployBase.DeployBase, DTQt5.Qt5Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
rootDir = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../..'))
|
||||
self.setRootDir(rootDir)
|
||||
self.targetSystem = 'posix_windows'
|
||||
self.installDir = os.path.join(self.buildDir, 'ports/deploy/temp_priv')
|
||||
self.pkgsDir = os.path.join(self.buildDir, 'ports/deploy/packages_auto/windows')
|
||||
|
@ -46,8 +48,8 @@ class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
|||
self.programName = os.path.splitext(os.path.basename(self.mainBinary))[0]
|
||||
self.programVersion = self.detectVersion(os.path.join(self.rootDir, 'commons.pri'))
|
||||
self.detectMake()
|
||||
self.binarySolver = tools.binary_pecoff.DeployToolsBinary()
|
||||
self.binarySolver.readExcludeList(os.path.join(self.rootDir, 'ports/deploy/tools/exclude/exclude.{}.{}.txt'.format(os.name, sys.platform)))
|
||||
self.binarySolver = DTBinaryPecoff.PecoffBinaryTools()
|
||||
self.binarySolver.readExcludes(os.name, sys.platform)
|
||||
self.packageConfig = os.path.join(self.rootDir, 'ports/deploy/package_info.conf')
|
||||
self.dependencies = []
|
||||
self.installerConfig = os.path.join(self.installDir, 'installer/config')
|
||||
|
|
|
@ -27,14 +27,16 @@ import sys
|
|||
import threading
|
||||
import zipfile
|
||||
|
||||
import deploy_base
|
||||
import tools.binary_pecoff
|
||||
import tools.qt5
|
||||
from WebcamoidDeployTools import DTDeployBase
|
||||
from WebcamoidDeployTools import DTQt5
|
||||
from WebcamoidDeployTools import DTBinaryPecoff
|
||||
|
||||
|
||||
class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
||||
class Deploy(DTDeployBase.DeployBase, DTQt5.Qt5Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
rootDir = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../..'))
|
||||
self.setRootDir(rootDir)
|
||||
self.installDir = os.path.join(self.buildDir, 'ports/deploy/temp_priv')
|
||||
self.pkgsDir = os.path.join(self.buildDir, 'ports/deploy/packages_auto/windows')
|
||||
self.detectQt(os.path.join(self.buildDir, 'Manager'))
|
||||
|
@ -46,8 +48,8 @@ class Deploy(deploy_base.DeployBase, tools.qt5.DeployToolsQt):
|
|||
self.programName = os.path.splitext(os.path.basename(self.mainBinary))[0]
|
||||
self.programVersion = self.detectVersion(os.path.join(self.rootDir, 'commons.pri'))
|
||||
self.detectMake()
|
||||
self.binarySolver = tools.binary_pecoff.DeployToolsBinary()
|
||||
self.binarySolver.readExcludeList(os.path.join(self.rootDir, 'ports/deploy/tools/exclude/exclude.{}.{}.txt'.format(os.name, sys.platform)))
|
||||
self.binarySolver = DTBinaryPecoff.PecoffBinaryTools()
|
||||
self.binarySolver.readExcludes(os.name, sys.platform)
|
||||
self.packageConfig = os.path.join(self.rootDir, 'ports/deploy/package_info.conf')
|
||||
self.dependencies = []
|
||||
self.installerConfig = os.path.join(self.installDir, 'installer/config')
|
||||
|
|
|
@ -1,199 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2019 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import tools.utils
|
||||
|
||||
|
||||
class AndroidTools(tools.utils.DeployToolsUtils):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.androidPlatform = ''
|
||||
self.archMap = [('arm64-v8a' , 'aarch64', 'aarch64-linux-android'),
|
||||
('armeabi-v7a', 'arm' , 'arm-linux-androideabi'),
|
||||
('x86' , 'i686' , 'i686-linux-android' ),
|
||||
('x86_64' , 'x86_64' , 'x86_64-linux-android' )]
|
||||
self.androidSDK = ''
|
||||
self.androidNDK = ''
|
||||
self.bundledInLib = []
|
||||
self.qtLibs = []
|
||||
self.localLibs = []
|
||||
|
||||
if 'ANDROID_HOME' in os.environ:
|
||||
self.androidSDK = os.environ['ANDROID_HOME']
|
||||
|
||||
if 'ANDROID_NDK_ROOT' in os.environ:
|
||||
self.androidNDK = os.environ['ANDROID_NDK_ROOT']
|
||||
elif 'ANDROID_NDK' in os.environ:
|
||||
self.androidNDK = os.environ['ANDROID_NDK']
|
||||
|
||||
def detectAndroidPlatform(self, path=''):
|
||||
for makeFile in self.detectMakeFiles(path):
|
||||
with open(makeFile) as f:
|
||||
for line in f:
|
||||
if line.startswith('DESTDIR') and '=' in line:
|
||||
buildPath = os.path.join(path, line.split('=')[1].strip())
|
||||
chunks = [part for part in buildPath.split(os.path.sep) if len(part) > 0]
|
||||
|
||||
if len(chunks) >= 2:
|
||||
self.androidPlatform = chunks[len(chunks) - 2]
|
||||
|
||||
return
|
||||
|
||||
def detectLibPaths(self):
|
||||
if len(self.androidNDK) < 1:
|
||||
return []
|
||||
|
||||
for arch in self.archMap:
|
||||
if self.targetArch == arch[0]:
|
||||
return [os.path.join(self.androidNDK,
|
||||
'toolchains',
|
||||
'llvm',
|
||||
'prebuilt',
|
||||
'linux-x86_64',
|
||||
'sysroot',
|
||||
'usr',
|
||||
'lib',
|
||||
arch[1] + '-linux-android')]
|
||||
|
||||
return []
|
||||
|
||||
def detectBinPaths(self):
|
||||
if len(self.androidNDK) < 1:
|
||||
return []
|
||||
|
||||
for arch in self.archMap:
|
||||
if self.targetArch == arch[0]:
|
||||
binPath = os.path.join(self.androidNDK,
|
||||
'toolchains',
|
||||
'llvm',
|
||||
'prebuilt',
|
||||
'linux-x86_64',
|
||||
arch[2],
|
||||
'bin')
|
||||
|
||||
return [binPath]
|
||||
|
||||
return []
|
||||
|
||||
def fixQtLibs(self):
|
||||
for root, dirs, files in os.walk(self.assetsIntallDir):
|
||||
for f in files:
|
||||
if f.endswith('.so'):
|
||||
srcPath = os.path.join(root, f)
|
||||
relPath = root.replace(self.assetsIntallDir, '')[1:]
|
||||
prefix = 'lib' + relPath.replace(os.path.sep, '_') + '_'
|
||||
lib = ''
|
||||
|
||||
if f.startswith(prefix):
|
||||
lib = f
|
||||
else:
|
||||
lib = prefix + f
|
||||
|
||||
dstPath = os.path.join(self.libInstallDir, lib)
|
||||
print(' {} -> {}'.format(srcPath, dstPath))
|
||||
self.move(srcPath, dstPath)
|
||||
self.bundledInLib += [(lib, os.path.join(relPath, f))]
|
||||
|
||||
def libBaseName(self, lib):
|
||||
basename = os.path.basename(lib)
|
||||
|
||||
return basename[3: len(basename) - 3]
|
||||
|
||||
def fixLibsXml(self):
|
||||
bundledInAssets = []
|
||||
assetsDir = os.path.join(self.rootInstallDir, 'assets')
|
||||
|
||||
for root, dirs, files in os.walk(assetsDir):
|
||||
for f in files:
|
||||
srcPath = os.path.join(root.replace(assetsDir, '')[1:], f)
|
||||
dstPath = os.path.sep.join(srcPath.split(os.path.sep)[1:])
|
||||
|
||||
if (len(dstPath) > 0):
|
||||
bundledInAssets += [(srcPath, dstPath)]
|
||||
|
||||
libsXml = os.path.join(self.rootInstallDir, 'res', 'values', 'libs.xml')
|
||||
libsXmlTemp = os.path.join(self.rootInstallDir, 'res', 'values', 'libsTemp.xml')
|
||||
|
||||
tree = ET.parse(libsXml)
|
||||
root = tree.getroot()
|
||||
oldFeatures = set()
|
||||
oldPermissions = set()
|
||||
resources = {}
|
||||
|
||||
for array in root:
|
||||
if not array.attrib['name'] in resources:
|
||||
resources[array.attrib['name']] = set()
|
||||
|
||||
for item in array:
|
||||
if item.text:
|
||||
lib = item.text.strip()
|
||||
|
||||
if len(lib) > 0:
|
||||
lib = '<item>{}</item>'.format(lib)
|
||||
resources[array.attrib['name']].add(lib)
|
||||
|
||||
qtLibs = set(['<item>{};{}</item>'.format(self.targetArch, self.libBaseName(lib)) for lib in self.qtLibs])
|
||||
|
||||
if 'qt_libs' in resources:
|
||||
qtLibs -= resources['qt_libs']
|
||||
|
||||
qtLibs = '\n'.join(sorted(list(qtLibs)))
|
||||
bundledInLib = set(['<item>{}:{}</item>'.format(lib[0], lib[1]) for lib in self.bundledInLib])
|
||||
|
||||
if 'bundled_in_lib' in resources:
|
||||
bundledInLib -= resources['bundled_in_lib']
|
||||
|
||||
bundledInLib = '\n'.join(sorted(list(bundledInLib)))
|
||||
bundledInAssets = set(['<item>{}:{}</item>'.format(lib[0], lib[1]) for lib in bundledInAssets])
|
||||
|
||||
if 'bundled_in_assets' in resources:
|
||||
bundledInAssets -= resources['bundled_in_assets']
|
||||
|
||||
bundledInAssets = '\n'.join(sorted(list(bundledInAssets)))
|
||||
|
||||
localLibs = sorted(list(set(self.localLibs)))
|
||||
localLibs = set(['<item>{};{}</item>'.format(self.targetArch, ':'.join(localLibs)) for lib in localLibs])
|
||||
|
||||
if 'load_local_libs' in resources:
|
||||
localLibs -= resources['load_local_libs']
|
||||
|
||||
localLibs = '\n'.join(sorted(list(localLibs)))
|
||||
|
||||
replace = {'<!-- %%INSERT_EXTRA_LIBS%% -->' : '',
|
||||
'<!-- %%INSERT_QT_LIBS%% -->' : qtLibs,
|
||||
'<!-- %%INSERT_BUNDLED_IN_LIB%% -->' : bundledInLib,
|
||||
'<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->': bundledInAssets,
|
||||
'<!-- %%INSERT_LOCAL_LIBS%% -->' : localLibs}
|
||||
|
||||
with open(libsXml) as inFile:
|
||||
with open(libsXmlTemp, 'w') as outFile:
|
||||
for line in inFile:
|
||||
for key in replace:
|
||||
line = line.replace(key, replace[key])
|
||||
|
||||
outFile.write(line)
|
||||
|
||||
os.remove(libsXml)
|
||||
shutil.move(libsXmlTemp, libsXml)
|
|
@ -1,169 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess # nosec
|
||||
import threading
|
||||
import time
|
||||
|
||||
import tools
|
||||
|
||||
|
||||
class DeployToolsBinary(tools.utils.DeployToolsUtils):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.detectStrip()
|
||||
self.excludes = []
|
||||
|
||||
def isValid(self, path):
|
||||
return False
|
||||
|
||||
def find(self, path):
|
||||
binaries = []
|
||||
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
binaryPath = os.path.join(root, f)
|
||||
|
||||
if not os.path.islink(binaryPath) and self.isValid(binaryPath):
|
||||
binaries.append(binaryPath)
|
||||
|
||||
return binaries
|
||||
|
||||
def dump(self, binary):
|
||||
return {}
|
||||
|
||||
def dependencies(self, binary):
|
||||
return []
|
||||
|
||||
def allDependencies(self, binary):
|
||||
deps = self.dependencies(binary)
|
||||
solved = set()
|
||||
|
||||
while len(deps) > 0:
|
||||
dep = deps.pop()
|
||||
|
||||
for binDep in self.dependencies(dep):
|
||||
if binDep != dep and not binDep in solved:
|
||||
deps.append(binDep)
|
||||
|
||||
if self.system == 'mac':
|
||||
i = dep.rfind('.framework/')
|
||||
|
||||
if i >= 0:
|
||||
dep = dep[: i] + '.framework'
|
||||
|
||||
solved.add(dep)
|
||||
|
||||
return solved
|
||||
|
||||
def scanDependencies(self, path):
|
||||
deps = set()
|
||||
|
||||
for binPath in self.find(path):
|
||||
for dep in self.allDependencies(binPath):
|
||||
deps.add(dep)
|
||||
|
||||
return sorted(deps)
|
||||
|
||||
def name(self, binary):
|
||||
return ''
|
||||
|
||||
def detectStrip(self):
|
||||
self.stripBin = self.whereBin('strip.exe' if self.system == 'windows' else 'strip')
|
||||
|
||||
def strip(self, binary):
|
||||
if self.stripBin == '':
|
||||
return
|
||||
|
||||
process = subprocess.Popen([self.stripBin, binary], # nosec
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
process.communicate()
|
||||
|
||||
def stripSymbols(self, path):
|
||||
threads = []
|
||||
|
||||
for binary in self.find(path):
|
||||
thread = threading.Thread(target=self.strip, args=(binary,))
|
||||
threads.append(thread)
|
||||
|
||||
while threading.active_count() >= self.njobs:
|
||||
time.sleep(0.25)
|
||||
|
||||
thread.start()
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
def readExcludeList(self, excludeList):
|
||||
self.excludes = []
|
||||
|
||||
if os.path.exists(excludeList):
|
||||
with open(excludeList) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
|
||||
if len(line) > 0 and line[0] != '#':
|
||||
i = line.find('#')
|
||||
|
||||
if i >= 0:
|
||||
line = line[: i]
|
||||
|
||||
line = line.strip()
|
||||
|
||||
if len(line) > 0:
|
||||
self.excludes.append(line)
|
||||
|
||||
def isExcluded(self, path):
|
||||
for exclude in self.excludes:
|
||||
if self.targetSystem == 'windows' or self.targetSystem == 'posix_windows':
|
||||
path = path.lower().replace('\\', '/')
|
||||
exclude = exclude.lower()
|
||||
|
||||
if re.fullmatch(exclude, path):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def resetFilePermissions(self, rootPath, binariesPath):
|
||||
for root, dirs, files in os.walk(rootPath):
|
||||
for d in dirs:
|
||||
permissions = 0o755
|
||||
path = os.path.join(root, d)
|
||||
|
||||
if self.system == 'mac':
|
||||
os.chmod(path, permissions, follow_symlinks=False)
|
||||
else:
|
||||
os.chmod(path, permissions)
|
||||
|
||||
for f in files:
|
||||
permissions = 0o644
|
||||
path = os.path.join(root, f)
|
||||
|
||||
if root == binariesPath and self.isValid(path):
|
||||
permissions = 0o744
|
||||
|
||||
if self.system == 'mac':
|
||||
os.chmod(path, permissions, follow_symlinks=False)
|
||||
else:
|
||||
os.chmod(path, permissions)
|
|
@ -1,345 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import fnmatch
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import tools.binary
|
||||
|
||||
|
||||
class DeployToolsBinary(tools.binary.DeployToolsBinary):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.ldLibraryPath = os.environ['LD_LIBRARY_PATH'].split(':') if 'LD_LIBRARY_PATH' in os.environ else []
|
||||
self.libsSeachPaths = self.readLdconf() \
|
||||
+ ['/usr/lib',
|
||||
'/usr/lib64',
|
||||
'/lib',
|
||||
'/lib64',
|
||||
'/usr/local/lib',
|
||||
'/usr/local/lib64']
|
||||
self.emCodes = {3 : '386',
|
||||
40 : 'ARM',
|
||||
62 : 'X86_64',
|
||||
183: 'AARCH64'}
|
||||
|
||||
def readLdconf(self, ldconf='/etc/ld.so.conf'):
|
||||
if not os.path.exists(ldconf):
|
||||
return []
|
||||
|
||||
confDir = os.path.dirname(ldconf)
|
||||
libpaths = []
|
||||
|
||||
with open(ldconf) as f:
|
||||
for line in f:
|
||||
i = line.find('#')
|
||||
|
||||
if i == 0:
|
||||
continue
|
||||
|
||||
if i >= 0:
|
||||
line = line[: i]
|
||||
|
||||
line = line.strip()
|
||||
|
||||
if len(line) < 1:
|
||||
continue
|
||||
|
||||
if line.startswith('include'):
|
||||
conf = line.split()[1]
|
||||
|
||||
if not conf.startswith('/'):
|
||||
conf = os.path.join(confDir, conf)
|
||||
|
||||
dirname = os.path.dirname(conf)
|
||||
|
||||
if os.path.exists(dirname):
|
||||
for f in os.listdir(dirname):
|
||||
path = os.path.join(dirname, f)
|
||||
|
||||
if fnmatch.fnmatch(path, conf):
|
||||
libpaths += self.readLdconf(path)
|
||||
else:
|
||||
libpaths.append(line)
|
||||
|
||||
return libpaths
|
||||
|
||||
def isValid(self, path):
|
||||
with open(path, 'rb') as f:
|
||||
return f.read(4) == b'\x7fELF'
|
||||
|
||||
@staticmethod
|
||||
def readString(f):
|
||||
s = b''
|
||||
|
||||
while True:
|
||||
c = f.read(1)
|
||||
|
||||
if c == b'\x00':
|
||||
break
|
||||
|
||||
s += c
|
||||
|
||||
return s
|
||||
|
||||
@staticmethod
|
||||
def readNumber(f, arch):
|
||||
if arch == '32bits':
|
||||
return struct.unpack('I', f.read(4))[0]
|
||||
|
||||
return struct.unpack('Q', f.read(8))[0]
|
||||
|
||||
@staticmethod
|
||||
def readDynamicEntry(f, arch):
|
||||
if arch == '32bits':
|
||||
return struct.unpack('iI', f.read(8))
|
||||
|
||||
return struct.unpack('qQ', f.read(16))
|
||||
|
||||
# https://refspecs.linuxfoundation.org/lsb.shtml (See Core, Generic)
|
||||
# https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||
def dump(self, binary):
|
||||
# ELF file magic
|
||||
ELFMAGIC = b'\x7fELF'
|
||||
|
||||
# Sections
|
||||
SHT_STRTAB = 0x3
|
||||
SHT_DYNAMIC = 0x6
|
||||
|
||||
# Dynamic section entries
|
||||
DT_NULL = 0
|
||||
DT_NEEDED = 1
|
||||
DT_RPATH = 15
|
||||
DT_RUNPATH = 0x1d
|
||||
|
||||
with open(binary, 'rb') as f:
|
||||
# Read magic signature.
|
||||
magic = f.read(4)
|
||||
|
||||
if magic != ELFMAGIC:
|
||||
return {}
|
||||
|
||||
# Read the data structure of the file.
|
||||
eiClass = '32bits' if struct.unpack('B', f.read(1))[0] == 1 else '64bits'
|
||||
|
||||
# Read machine code.
|
||||
f.seek(0x12, os.SEEK_SET)
|
||||
machine = struct.unpack('H', f.read(2))[0]
|
||||
|
||||
# Get a pointer to the sections table.
|
||||
sectionHeaderTable = 0
|
||||
|
||||
if eiClass == '32bits':
|
||||
f.seek(0x20, os.SEEK_SET)
|
||||
sectionHeaderTable = self.readNumber(f, eiClass)
|
||||
f.seek(0x30, os.SEEK_SET)
|
||||
else:
|
||||
f.seek(0x28, os.SEEK_SET)
|
||||
sectionHeaderTable = self.readNumber(f, eiClass)
|
||||
f.seek(0x3c, os.SEEK_SET)
|
||||
|
||||
# Read the number of sections.
|
||||
nSections = struct.unpack('H', f.read(2))[0]
|
||||
|
||||
# Read the index of the string table that stores sections names.
|
||||
shstrtabIndex = struct.unpack('H', f.read(2))[0]
|
||||
|
||||
# Read sections.
|
||||
f.seek(sectionHeaderTable, os.SEEK_SET)
|
||||
neededPtr = []
|
||||
rpathsPtr = []
|
||||
runpathsPtr = []
|
||||
strtabs = []
|
||||
shstrtab = []
|
||||
|
||||
for section in range(nSections):
|
||||
sectionStart = f.tell()
|
||||
|
||||
# Read the a pointer to the virtual address in the string table
|
||||
# that contains the name of this section.
|
||||
sectionName = struct.unpack('I', f.read(4))[0]
|
||||
|
||||
# Read the type of this section.
|
||||
sectionType = struct.unpack('I', f.read(4))[0]
|
||||
|
||||
# Read the virtual address of this section.
|
||||
f.seek(sectionStart + (0x0c if eiClass == '32bits' else 0x10), os.SEEK_SET)
|
||||
shAddr = self.readNumber(f, eiClass)
|
||||
|
||||
# Read the offset in file to this section.
|
||||
shOffset = self.readNumber(f, eiClass)
|
||||
f.seek(shOffset, os.SEEK_SET)
|
||||
|
||||
if sectionType == SHT_DYNAMIC:
|
||||
# Read dynamic sections.
|
||||
while True:
|
||||
# Read dynamic entries.
|
||||
dTag, dVal = self.readDynamicEntry(f, eiClass)
|
||||
|
||||
if dTag == DT_NULL:
|
||||
# End of dynamic sections.
|
||||
break
|
||||
elif dTag == DT_NEEDED:
|
||||
# Dynamically imported libraries.
|
||||
neededPtr.append(dVal)
|
||||
elif dTag == DT_RPATH:
|
||||
# RPATHs.
|
||||
rpathsPtr.append(dVal)
|
||||
elif dTag == DT_RUNPATH:
|
||||
# RUNPATHs.
|
||||
runpathsPtr.append(dVal)
|
||||
elif sectionType == SHT_STRTAB:
|
||||
# Read string tables.
|
||||
if section == shstrtabIndex:
|
||||
# We found the string table that stores sections names.
|
||||
shstrtab = [shAddr, shOffset]
|
||||
else:
|
||||
# Save string tables for later usage.
|
||||
strtabs += [[sectionName, shAddr, shOffset]]
|
||||
|
||||
# Move to next section.
|
||||
f.seek(sectionStart + (0x28 if eiClass == '32bits' else 0x40), os.SEEK_SET)
|
||||
|
||||
# Libraries names and RUNPATHs are located in '.dynstr' table.
|
||||
strtab = []
|
||||
|
||||
for tab in strtabs:
|
||||
f.seek(tab[0] - shstrtab[0] + shstrtab[1], os.SEEK_SET)
|
||||
|
||||
if self.readString(f) == b'.dynstr':
|
||||
strtab = tab
|
||||
|
||||
# Read dynamically imported libraries.
|
||||
needed = set()
|
||||
|
||||
for lib in neededPtr:
|
||||
f.seek(lib + strtab[2], os.SEEK_SET)
|
||||
needed.add(self.readString(f).decode(sys.getdefaultencoding()))
|
||||
|
||||
# Read RPATHs
|
||||
rpaths = set()
|
||||
|
||||
for path in rpathsPtr:
|
||||
f.seek(path + strtab[2], os.SEEK_SET)
|
||||
rpaths.add(self.readString(f).decode(sys.getdefaultencoding()))
|
||||
|
||||
# Read RUNPATHs
|
||||
runpaths = set()
|
||||
|
||||
for path in runpathsPtr:
|
||||
f.seek(path + strtab[2], os.SEEK_SET)
|
||||
runpaths.add(self.readString(f).decode(sys.getdefaultencoding()))
|
||||
|
||||
return {'machine': machine,
|
||||
'imports': needed,
|
||||
'rpath': rpaths,
|
||||
'runpath': runpaths}
|
||||
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def readRpaths(elfInfo, binDir):
|
||||
rpaths = []
|
||||
runpaths = []
|
||||
|
||||
# http://amir.rachum.com/blog/2016/09/17/shared-libraries/
|
||||
for rpath in ['rpath', 'runpath']:
|
||||
for path in elfInfo[rpath]:
|
||||
if '$ORIGIN' in path:
|
||||
path = path.replace('$ORIGIN', binDir)
|
||||
|
||||
if not path.startswith('/'):
|
||||
path = os.path.join(binDir, path)
|
||||
|
||||
path = os.path.normpath(path)
|
||||
|
||||
if rpath == 'rpath':
|
||||
rpaths.append(path)
|
||||
else:
|
||||
runpaths.append(path)
|
||||
|
||||
return rpaths, runpaths
|
||||
|
||||
def libPath(self, lib, machine, rpaths, runpaths):
|
||||
# man ld.so
|
||||
searchPaths = rpaths \
|
||||
+ self.ldLibraryPath \
|
||||
+ runpaths \
|
||||
+ self.libsSeachPaths
|
||||
|
||||
for libdir in searchPaths:
|
||||
path = os.path.join(libdir, lib)
|
||||
|
||||
if os.path.exists(path):
|
||||
depElfInfo = self.dump(path)
|
||||
|
||||
if depElfInfo:
|
||||
if 'machine' in depElfInfo and (machine == 0 or depElfInfo['machine'] == machine):
|
||||
return path
|
||||
elif 'links' in depElfInfo and len(depElfInfo['links']) > 0:
|
||||
return path
|
||||
|
||||
return ''
|
||||
|
||||
def dependencies(self, binary):
|
||||
elfInfo = self.dump(binary)
|
||||
|
||||
if not elfInfo:
|
||||
return []
|
||||
|
||||
rpaths, runpaths = self.readRpaths(elfInfo, os.path.dirname(binary))
|
||||
libs = []
|
||||
deps = []
|
||||
|
||||
if 'imports' in elfInfo:
|
||||
deps = elfInfo['imports']
|
||||
elif 'links' in elfInfo:
|
||||
deps = elfInfo['links']
|
||||
|
||||
machine = 0
|
||||
|
||||
if 'machine' in elfInfo:
|
||||
machine = elfInfo['machine']
|
||||
|
||||
for lib in deps:
|
||||
libpath = self.libPath(lib, machine, rpaths, runpaths)
|
||||
|
||||
if len(libpath) > 0 and not self.isExcluded(libpath):
|
||||
libs.append(libpath)
|
||||
|
||||
return libs
|
||||
|
||||
def name(self, binary):
|
||||
dep = os.path.basename(binary)[3:]
|
||||
|
||||
return dep[: dep.find('.')]
|
||||
|
||||
def machineEMCode(self, binary):
|
||||
info = self.dump(binary)
|
||||
|
||||
if 'machine' in info:
|
||||
if info['machine'] in self.emCodes:
|
||||
return self.emCodes[info['machine']]
|
||||
|
||||
return 'UNKNOWN'
|
|
@ -1,182 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import tools.binary
|
||||
|
||||
|
||||
class DeployToolsBinary(tools.binary.DeployToolsBinary):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 32 bits magic number.
|
||||
self.MH_MAGIC = 0xfeedface # Native endian
|
||||
self.MH_CIGAM = 0xcefaedfe # Reverse endian
|
||||
|
||||
# 64 bits magic number.
|
||||
self.MH_MAGIC_64 = 0xfeedfacf # Native endian
|
||||
self.MH_CIGAM_64 = 0xcffaedfe # Reverse endian
|
||||
|
||||
def isValid(self, path):
|
||||
try:
|
||||
with open(path, 'rb') as f:
|
||||
# Read magic number.
|
||||
magic = struct.unpack('I', f.read(4))[0]
|
||||
|
||||
if magic == self.MH_MAGIC \
|
||||
or magic == self.MH_CIGAM \
|
||||
or magic == self.MH_MAGIC_64 \
|
||||
or magic == self.MH_CIGAM_64:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
# https://github.com/aidansteele/osx-abi-macho-file-format-reference
|
||||
def dump(self, binary):
|
||||
# Commands definitions
|
||||
LC_REQ_DYLD = 0x80000000
|
||||
LC_LOAD_DYLIB = 0xc
|
||||
LC_RPATH = 0x1c | LC_REQ_DYLD
|
||||
LC_ID_DYLIB = 0xd
|
||||
|
||||
dylibImports = []
|
||||
rpaths = []
|
||||
dylibId = ''
|
||||
|
||||
with open(binary, 'rb') as f:
|
||||
# Read magic number.
|
||||
magic = struct.unpack('I', f.read(4))[0]
|
||||
|
||||
if magic == self.MH_MAGIC or magic == self.MH_CIGAM:
|
||||
is32bits = True
|
||||
elif magic == self.MH_MAGIC_64 or magic == self.MH_CIGAM_64:
|
||||
is32bits = False
|
||||
else:
|
||||
return {}
|
||||
|
||||
# Read number of commands.
|
||||
f.seek(12, os.SEEK_CUR)
|
||||
ncmds = struct.unpack('I', f.read(4))[0]
|
||||
|
||||
# Move to load commands
|
||||
f.seek(8 if is32bits else 12, os.SEEK_CUR)
|
||||
|
||||
for _ in range(ncmds):
|
||||
# Read a load command and store it's position in the file.
|
||||
loadCommandStart = f.tell()
|
||||
loadCommand = struct.unpack('II', f.read(8))
|
||||
|
||||
# If the command list a library
|
||||
if loadCommand[0] in [LC_LOAD_DYLIB, LC_RPATH, LC_ID_DYLIB]:
|
||||
# Save the position of the next command.
|
||||
nextCommand = f.tell() + loadCommand[1] - 8
|
||||
|
||||
# Move to the string
|
||||
f.seek(loadCommandStart + struct.unpack('I', f.read(4))[0], os.SEEK_SET)
|
||||
dylib = b''
|
||||
|
||||
# Read string until null character.
|
||||
while True:
|
||||
c = f.read(1)
|
||||
|
||||
if c == b'\x00':
|
||||
break
|
||||
|
||||
dylib += c
|
||||
s = dylib.decode(sys.getdefaultencoding())
|
||||
|
||||
if loadCommand[0] == LC_LOAD_DYLIB:
|
||||
dylibImports.append(s)
|
||||
elif loadCommand[0] == LC_RPATH:
|
||||
rpaths.append(s)
|
||||
elif loadCommand[0] == LC_ID_DYLIB:
|
||||
dylibId = s
|
||||
|
||||
f.seek(nextCommand, os.SEEK_SET)
|
||||
else:
|
||||
f.seek(loadCommand[1] - 8, os.SEEK_CUR)
|
||||
|
||||
return {'imports': dylibImports, 'rpaths': rpaths, 'id': dylibId}
|
||||
|
||||
@staticmethod
|
||||
def solveRefpath(path):
|
||||
if not path.startswith('@'):
|
||||
return path
|
||||
|
||||
searchPaths = []
|
||||
|
||||
if 'DYLD_LIBRARY_PATH' in os.environ:
|
||||
searchPaths += os.environ['DYLD_LIBRARY_PATH'].split(':')
|
||||
|
||||
if 'DYLD_FRAMEWORK_PATH' in os.environ:
|
||||
searchPaths += os.environ['DYLD_FRAMEWORK_PATH'].split(':')
|
||||
|
||||
if path.endswith('.dylib'):
|
||||
dep = os.path.basename(path)
|
||||
else:
|
||||
i = path.rfind(os.sep, 0, path.rfind('.framework'))
|
||||
dep = path[i + 1:]
|
||||
|
||||
for fpath in searchPaths:
|
||||
realPath = os.path.join(fpath, dep)
|
||||
|
||||
if os.path.exists(realPath):
|
||||
return realPath
|
||||
|
||||
return ''
|
||||
|
||||
def dependencies(self, binary):
|
||||
machInfo = self.dump(binary)
|
||||
|
||||
if not machInfo:
|
||||
return []
|
||||
|
||||
libs = []
|
||||
|
||||
for mach in machInfo['imports']:
|
||||
mach = self.solveRefpath(mach)
|
||||
|
||||
if mach == '' or self.isExcluded(mach) or not os.path.exists(mach):
|
||||
continue
|
||||
|
||||
dirName = os.path.dirname(mach)
|
||||
dirName = os.path.realpath(dirName)
|
||||
baseName = os.path.basename(mach)
|
||||
libs.append(os.path.join(dirName, baseName))
|
||||
|
||||
return libs
|
||||
|
||||
def name(self, binary):
|
||||
dep = os.path.basename(binary)
|
||||
i = dep.find('.')
|
||||
|
||||
if i >= 0:
|
||||
dep = dep[: dep.find('.')]
|
||||
|
||||
if 'Qt' in dep and not 'Qt5' in dep:
|
||||
dep = dep.replace('Qt', 'Qt5')
|
||||
|
||||
return dep
|
|
@ -1,179 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import tools.binary
|
||||
|
||||
|
||||
class DeployToolsBinary(tools.binary.DeployToolsBinary):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def isValid(self, path):
|
||||
mimetype, _ = mimetypes.guess_type(path)
|
||||
|
||||
if mimetype == 'application/x-msdownload':
|
||||
return True
|
||||
|
||||
if mimetype != 'application/octet-stream':
|
||||
return False
|
||||
|
||||
with open(path, 'rb') as f:
|
||||
if f.read(2) != b'MZ':
|
||||
return []
|
||||
|
||||
f.seek(0x3c, os.SEEK_SET)
|
||||
peHeaderOffset = struct.unpack('I', f.read(4))
|
||||
f.seek(peHeaderOffset[0], os.SEEK_SET)
|
||||
peSignatue = f.read(4)
|
||||
|
||||
if peSignatue != b'PE\x00\x00':
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680547(v=vs.85).aspx
|
||||
# https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
|
||||
def dump(self, binary):
|
||||
dllImports = set()
|
||||
|
||||
if not os.path.exists(binary) or not os.path.isfile(binary):
|
||||
return dllImports
|
||||
|
||||
with open(binary, 'rb') as f:
|
||||
# Check DOS header signature.
|
||||
if f.read(2) != b'MZ':
|
||||
return []
|
||||
|
||||
# Move to COFF header.
|
||||
f.seek(0x3c, os.SEEK_SET)
|
||||
peHeaderOffset = struct.unpack('I', f.read(4))
|
||||
f.seek(peHeaderOffset[0], os.SEEK_SET)
|
||||
peSignatue = f.read(4)
|
||||
|
||||
# Check COFF header signature.
|
||||
if peSignatue != b'PE\x00\x00':
|
||||
return []
|
||||
|
||||
# Read COFF header.
|
||||
coffHeader = struct.unpack('HHIIIHH', f.read(20))
|
||||
nSections = coffHeader[1]
|
||||
sectionTablePos = coffHeader[5] + f.tell()
|
||||
|
||||
# Read magic signature in standard COFF fields.
|
||||
peType = 'PE32' if f.read(2) == b'\x0b\x01' else 'PE32+'
|
||||
|
||||
# Move to data directories.
|
||||
f.seek(102 if peType == 'PE32' else 118, os.SEEK_CUR)
|
||||
|
||||
# Read the import table.
|
||||
importTablePos, importTableSize = struct.unpack('II', f.read(8))
|
||||
|
||||
# Move to Sections table.
|
||||
f.seek(sectionTablePos, os.SEEK_SET)
|
||||
sections = []
|
||||
idataTablePhysical = -1
|
||||
|
||||
# Search for 'idata' section.
|
||||
for _ in range(nSections):
|
||||
# Read section.
|
||||
section = struct.unpack('8pIIIIIIHHI', f.read(40))
|
||||
sectionName = section[0].replace(b'\x00', b'')
|
||||
|
||||
# Save a reference to the sections.
|
||||
sections += [section]
|
||||
|
||||
if sectionName == b'idata' or sectionName == b'rdata':
|
||||
idataTablePhysical = section[4]
|
||||
|
||||
# If import table was defined calculate it's position in
|
||||
# the file in relation to the address given by 'idata'.
|
||||
if importTableSize > 0:
|
||||
idataTablePhysical += importTablePos - section[2]
|
||||
|
||||
if idataTablePhysical < 0:
|
||||
return []
|
||||
|
||||
# Move to 'idata' section.
|
||||
f.seek(idataTablePhysical, os.SEEK_SET)
|
||||
dllList = set()
|
||||
|
||||
# Read 'idata' directory table.
|
||||
while True:
|
||||
# Read DLL entries.
|
||||
try:
|
||||
dllImport = struct.unpack('IIIII', f.read(20))
|
||||
except:
|
||||
break
|
||||
|
||||
# Null directory entry.
|
||||
if dllImport[0] | dllImport[1] | dllImport[2] | dllImport[3] | dllImport[4] == 0:
|
||||
break
|
||||
|
||||
# Locate where is located the DLL name in relation to the
|
||||
# sections.
|
||||
for section in sections:
|
||||
if dllImport[3] >= section[2] \
|
||||
and dllImport[3] < section[1] + section[2]:
|
||||
dllList.add(dllImport[3] - section[2] + section[4])
|
||||
|
||||
break
|
||||
|
||||
for dll in dllList:
|
||||
# Move to DLL name.
|
||||
f.seek(dll, os.SEEK_SET)
|
||||
dllName = b''
|
||||
|
||||
# Read string until null character.
|
||||
while True:
|
||||
c = f.read(1)
|
||||
|
||||
if c == b'\x00':
|
||||
break
|
||||
|
||||
dllName += c
|
||||
|
||||
try:
|
||||
dllImports.add(dllName.decode(sys.getdefaultencoding()))
|
||||
except:
|
||||
pass
|
||||
|
||||
return dllImports
|
||||
|
||||
def dependencies(self, binary):
|
||||
deps = []
|
||||
|
||||
for dep in self.dump(binary):
|
||||
depPath = self.whereBin(dep)
|
||||
|
||||
if len(depPath) > 0 and not self.isExcluded(depPath):
|
||||
deps.append(depPath)
|
||||
|
||||
return deps
|
||||
|
||||
def name(self, binary):
|
||||
dep = os.path.basename(binary)
|
||||
|
||||
return dep[: dep.find('.')]
|
|
@ -1,2 +0,0 @@
|
|||
C:/Windows/System32/.*
|
||||
C:/Program Files/.*
|
|
@ -1,2 +0,0 @@
|
|||
/usr/lib/.*
|
||||
/System/Library/Frameworks/.*
|
|
@ -1,110 +0,0 @@
|
|||
# VDSO
|
||||
(.*/)*ld-linux.so.2
|
||||
(.*/)*ld-linux-x86-64.so.2
|
||||
|
||||
# Glibc
|
||||
(.*/)*libc.so.[0-9]+
|
||||
(.*/)*libdl.so.[0-9]+
|
||||
(.*/)*libm.so.[0-9]+
|
||||
(.*/)*libmvec.so.[0-9]+
|
||||
(.*/)*libpthread.so.[0-9]+
|
||||
(.*/)*libresolv.so.[0-9]+
|
||||
(.*/)*librt.so.[0-9]+
|
||||
|
||||
# GCC
|
||||
(.*/)*libgcc_s.so.[0-9]+
|
||||
(.*/)*libgomp.so.[0-9]+
|
||||
(.*/)*libstdc\+\+.so.[0-9]+
|
||||
|
||||
# Core libraries
|
||||
(.*/)*libSM.so.6
|
||||
(.*/)*libblkid.so.1
|
||||
(.*/)*libcap.so.2
|
||||
(.*/)*libcom_err.so.2
|
||||
(.*/)*libcrypto.so.1.0.0
|
||||
(.*/)*libdb-5.3.so
|
||||
(.*/)*libdbus-1.so.3
|
||||
(.*/)*libexpat.so.1
|
||||
(.*/)*libfontconfig.so.1
|
||||
(.*/)*libfreetype.so.6
|
||||
(.*/)*libgcrypt.so.20
|
||||
(.*/)*libgmp.so.10
|
||||
(.*/)*libgpg-error.so.0
|
||||
(.*/)*libgssapi_krb5.so.2
|
||||
(.*/)*libharfbuzz.so.0
|
||||
(.*/)*libk5crypto.so.3
|
||||
(.*/)*libkeyutils.so.1
|
||||
(.*/)*libkrb5.so.3
|
||||
(.*/)*libkrb5support.so.0
|
||||
(.*/)*liblz4.so.1
|
||||
(.*/)*liblzma.so.5
|
||||
(.*/)*libmount.so.1
|
||||
(.*/)*libpcre.so.[0-9]+$
|
||||
(.*/)*libpcre16.so.0
|
||||
(.*/)*libssh2.so.1
|
||||
(.*/)*libssl.so.1.0.0
|
||||
(.*/)*libtasn1.so.6
|
||||
(.*/)*libusb-1.0.so.0
|
||||
(.*/)*libuuid.so.1
|
||||
(.*/)*libz.so.1
|
||||
|
||||
# Glib2
|
||||
(.*/)*libgio-2.0.so.0
|
||||
(.*/)*libglib-2.0.so.0
|
||||
(.*/)*libgmodule-2.0.so.0
|
||||
(.*/)*libgobject-2.0.so.0
|
||||
|
||||
# X11
|
||||
(.*/)*libX11-xcb.so.[0-9]+
|
||||
(.*/)*libX11.so.[0-9]+
|
||||
(.*/)*libXau.so.[0-9]+
|
||||
(.*/)*libXcursor.so.[0-9]+
|
||||
(.*/)*libXdamage.so.[0-9]+
|
||||
(.*/)*libXdmcp.so.[0-9]+
|
||||
(.*/)*libXext.so.[0-9]+
|
||||
(.*/)*libXfixes.so.[0-9]+
|
||||
(.*/)*libXi.so.[0-9]+
|
||||
(.*/)*libXinerama.so.[0-9]+
|
||||
(.*/)*libXrandr.so.[0-9]+
|
||||
(.*/)*libXrender.so.[0-9]+
|
||||
(.*/)*libXss.so.[0-9]+
|
||||
(.*/)*libXv.so.[0-9]+
|
||||
(.*/)*libXxf86vm.so.[0-9]+
|
||||
(.*/)*libglapi.so.[0-9]+
|
||||
(.*/)*libp11-kit.so.[0-9]+
|
||||
(.*/)*libxcb-dri2.so.[0-9]+
|
||||
(.*/)*libxcb-dri3.so.[0-9]+
|
||||
(.*/)*libxcb-glx.so.[0-9]+
|
||||
(.*/)*libxcb-icccm.so.[0-9]+
|
||||
(.*/)*libxcb-image.so.[0-9]+
|
||||
(.*/)*libxcb-keysyms.so.[0-9]+
|
||||
(.*/)*libxcb-present.so.[0-9]+
|
||||
(.*/)*libxcb-randr.so.[0-9]+
|
||||
(.*/)*libxcb-render-util.so.[0-9]+
|
||||
(.*/)*libxcb-render.so.[0-9]+
|
||||
(.*/)*libxcb-shape.so.[0-9]+
|
||||
(.*/)*libxcb-shm.so.[0-9]+
|
||||
(.*/)*libxcb-sync.so.[0-9]+
|
||||
(.*/)*libxcb-util.so.[0-9]+
|
||||
(.*/)*libxcb-xfixes.so.[0-9]+
|
||||
(.*/)*libxcb-xinerama.so.[0-9]+
|
||||
(.*/)*libxcb-xkb.so.[0-9]+
|
||||
(.*/)*libxcb.so.[0-9]+
|
||||
(.*/)*libxkbcommon-x11.so.[0-9]+
|
||||
(.*/)*libxkbcommon.so.[0-9]+
|
||||
(.*/)*libxshmfence.so.[0-9]+
|
||||
|
||||
# OpenGL
|
||||
(.*/)*libdrm.so.2
|
||||
(.*/)*libgbm.so.1
|
||||
(.*/)*libEGL.so.1
|
||||
(.*/)*libGL.so.1
|
||||
(.*/)*libGLX.so.0
|
||||
(.*/)*libGLdispatch.so.0
|
||||
|
||||
# Use system library instead
|
||||
(.*/)*libasound.so.2
|
||||
(.*/)*libpulse.so.0
|
||||
(.*/)*libpulse-simple.so.0
|
||||
(.*/)*libjack.so.0
|
||||
(.*/)*libv4l2.so.0
|
|
@ -1,110 +0,0 @@
|
|||
# VDSO
|
||||
(.*/)*ld-linux.so.2
|
||||
(.*/)*ld-linux-x86-64.so.2
|
||||
|
||||
# Glibc
|
||||
(.*/)*libc.so.[0-9]+
|
||||
(.*/)*libdl.so.[0-9]+
|
||||
(.*/)*libm.so.[0-9]+
|
||||
(.*/)*libmvec.so.[0-9]+
|
||||
(.*/)*libpthread.so.[0-9]+
|
||||
(.*/)*libresolv.so.[0-9]+
|
||||
(.*/)*librt.so.[0-9]+
|
||||
|
||||
# GCC
|
||||
(.*/)*libgcc_s.so.[0-9]+
|
||||
(.*/)*libgomp.so.[0-9]+
|
||||
(.*/)*libstdc\+\+.so.[0-9]+
|
||||
|
||||
# Core libraries
|
||||
(.*/)*libSM.so.6
|
||||
(.*/)*libblkid.so.1
|
||||
(.*/)*libcap.so.2
|
||||
(.*/)*libcom_err.so.2
|
||||
(.*/)*libcrypto.so.1.0.0
|
||||
(.*/)*libdb-5.3.so
|
||||
(.*/)*libdbus-1.so.3
|
||||
(.*/)*libexpat.so.1
|
||||
(.*/)*libfontconfig.so.1
|
||||
(.*/)*libfreetype.so.6
|
||||
(.*/)*libgcrypt.so.20
|
||||
(.*/)*libgmp.so.10
|
||||
(.*/)*libgpg-error.so.0
|
||||
(.*/)*libgssapi_krb5.so.2
|
||||
(.*/)*libharfbuzz.so.0
|
||||
(.*/)*libk5crypto.so.3
|
||||
(.*/)*libkeyutils.so.1
|
||||
(.*/)*libkrb5.so.3
|
||||
(.*/)*libkrb5support.so.0
|
||||
(.*/)*liblz4.so.1
|
||||
(.*/)*liblzma.so.5
|
||||
(.*/)*libmount.so.1
|
||||
(.*/)*libpcre.so.[0-9]+$
|
||||
(.*/)*libpcre16.so.0
|
||||
(.*/)*libssh2.so.1
|
||||
(.*/)*libssl.so.1.0.0
|
||||
(.*/)*libtasn1.so.6
|
||||
(.*/)*libusb-1.0.so.0
|
||||
(.*/)*libuuid.so.1
|
||||
(.*/)*libz.so.1
|
||||
|
||||
# Glib2
|
||||
(.*/)*libgio-2.0.so.0
|
||||
(.*/)*libglib-2.0.so.0
|
||||
(.*/)*libgmodule-2.0.so.0
|
||||
(.*/)*libgobject-2.0.so.0
|
||||
|
||||
# X11
|
||||
(.*/)*libX11-xcb.so.[0-9]+
|
||||
(.*/)*libX11.so.[0-9]+
|
||||
(.*/)*libXau.so.[0-9]+
|
||||
(.*/)*libXcursor.so.[0-9]+
|
||||
(.*/)*libXdamage.so.[0-9]+
|
||||
(.*/)*libXdmcp.so.[0-9]+
|
||||
(.*/)*libXext.so.[0-9]+
|
||||
(.*/)*libXfixes.so.[0-9]+
|
||||
(.*/)*libXi.so.[0-9]+
|
||||
(.*/)*libXinerama.so.[0-9]+
|
||||
(.*/)*libXrandr.so.[0-9]+
|
||||
(.*/)*libXrender.so.[0-9]+
|
||||
(.*/)*libXss.so.[0-9]+
|
||||
(.*/)*libXv.so.[0-9]+
|
||||
(.*/)*libXxf86vm.so.[0-9]+
|
||||
(.*/)*libglapi.so.[0-9]+
|
||||
(.*/)*libp11-kit.so.[0-9]+
|
||||
(.*/)*libxcb-dri2.so.[0-9]+
|
||||
(.*/)*libxcb-dri3.so.[0-9]+
|
||||
(.*/)*libxcb-glx.so.[0-9]+
|
||||
(.*/)*libxcb-icccm.so.[0-9]+
|
||||
(.*/)*libxcb-image.so.[0-9]+
|
||||
(.*/)*libxcb-keysyms.so.[0-9]+
|
||||
(.*/)*libxcb-present.so.[0-9]+
|
||||
(.*/)*libxcb-randr.so.[0-9]+
|
||||
(.*/)*libxcb-render-util.so.[0-9]+
|
||||
(.*/)*libxcb-render.so.[0-9]+
|
||||
(.*/)*libxcb-shape.so.[0-9]+
|
||||
(.*/)*libxcb-shm.so.[0-9]+
|
||||
(.*/)*libxcb-sync.so.[0-9]+
|
||||
(.*/)*libxcb-util.so.[0-9]+
|
||||
(.*/)*libxcb-xfixes.so.[0-9]+
|
||||
(.*/)*libxcb-xinerama.so.[0-9]+
|
||||
(.*/)*libxcb-xkb.so.[0-9]+
|
||||
(.*/)*libxcb.so.[0-9]+
|
||||
(.*/)*libxkbcommon-x11.so.[0-9]+
|
||||
(.*/)*libxkbcommon.so.[0-9]+
|
||||
(.*/)*libxshmfence.so.[0-9]+
|
||||
|
||||
# OpenGL
|
||||
(.*/)*libdrm.so.2
|
||||
(.*/)*libgbm.so.1
|
||||
(.*/)*libEGL.so.1
|
||||
(.*/)*libGL.so.1
|
||||
(.*/)*libGLX.so.0
|
||||
(.*/)*libGLdispatch.so.0
|
||||
|
||||
# Use system library instead
|
||||
(.*/)*libasound.so.2
|
||||
(.*/)*libpulse.so.0
|
||||
(.*/)*libpulse-simple.so.0
|
||||
(.*/)*libjack.so.0
|
||||
(.*/)*libv4l2.so.0
|
|
@ -1,691 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess # nosec
|
||||
import sys
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import tools.utils
|
||||
|
||||
|
||||
class DeployToolsQt(tools.utils.DeployToolsUtils):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.qmake = ''
|
||||
self.qtIFW = ''
|
||||
self.qtIFWVersion = ''
|
||||
self.qtInstallBins = ''
|
||||
self.qtInstallQml = ''
|
||||
self.qtInstallPlugins = ''
|
||||
self.qmlRootDirs = []
|
||||
self.qmlInstallDir = ''
|
||||
self.dependencies = []
|
||||
self.binarySolver = None
|
||||
self.installerConfig = ''
|
||||
self.appIcon = ''
|
||||
self.installerRunProgram = ''
|
||||
self.adminRights = False
|
||||
|
||||
def detectQt(self, path=''):
|
||||
self.detectQmake(path)
|
||||
self.qtInstallBins = self.qmakeQuery(var='QT_INSTALL_BINS')
|
||||
self.qtInstallQml = self.qmakeQuery(var='QT_INSTALL_QML')
|
||||
self.qtInstallPlugins = self.qmakeQuery(var='QT_INSTALL_PLUGINS')
|
||||
self.detectQtIFW()
|
||||
self.detectQtIFWVersion()
|
||||
|
||||
def detectQmake(self, path=''):
|
||||
for makeFile in self.detectMakeFiles(path):
|
||||
with open(makeFile) as f:
|
||||
for line in f:
|
||||
if line.startswith('QMAKE') and '=' in line:
|
||||
self.qmake = line.split('=')[1].strip()
|
||||
|
||||
return
|
||||
|
||||
if 'QMAKE_PATH' in os.environ:
|
||||
self.qmake = os.environ['QMAKE_PATH']
|
||||
|
||||
def detectTargetBinaryFromQt5Make(self, path=''):
|
||||
for makeFile in self.detectMakeFiles(path):
|
||||
with open(makeFile) as f:
|
||||
for line in f:
|
||||
if line.startswith('TARGET') and '=' in line:
|
||||
return os.path.join(path, line.split('=')[1].strip())
|
||||
|
||||
return ''
|
||||
|
||||
def qmakeQuery(self, qmake='', var=''):
|
||||
if qmake == '':
|
||||
if 'QMAKE_PATH' in os.environ:
|
||||
qmake = os.environ['QMAKE_PATH']
|
||||
else:
|
||||
qmake = self.qmake
|
||||
|
||||
try:
|
||||
args = [qmake, '-query']
|
||||
|
||||
if var != '':
|
||||
args += [var]
|
||||
|
||||
process = subprocess.Popen(args, # nosec
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, _ = process.communicate()
|
||||
|
||||
return stdout.strip().decode(sys.getdefaultencoding())
|
||||
except:
|
||||
pass
|
||||
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
def detectVersion(proFile):
|
||||
if 'DAILY_BUILD' in os.environ:
|
||||
return 'daily'
|
||||
|
||||
verMaj = '0'
|
||||
verMin = '0'
|
||||
verPat = '0'
|
||||
|
||||
try:
|
||||
with open(proFile) as f:
|
||||
for line in f:
|
||||
if line.startswith('VER_MAJ') and '=' in line:
|
||||
verMaj = line.split('=')[1].strip()
|
||||
elif line.startswith('VER_MIN') and '=' in line:
|
||||
verMin = line.split('=')[1].strip()
|
||||
elif line.startswith('VER_PAT') and '=' in line:
|
||||
verPat = line.split('=')[1].strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
return verMaj + '.' + verMin + '.' + verPat
|
||||
|
||||
def detectQtIFW(self):
|
||||
if 'BINARYCREATOR' in os.environ:
|
||||
self.qtIFW = os.environ['BINARYCREATOR']
|
||||
|
||||
return
|
||||
|
||||
# Try official Qt binarycreator first because it is statically linked.
|
||||
|
||||
if self.targetSystem == 'windows':
|
||||
homeQt = 'C:\\Qt'
|
||||
elif self.targetSystem == 'posix_windows':
|
||||
if 'WINEPREFIX' in os.environ:
|
||||
homeQt = os.path.expanduser(os.path.join(os.environ['WINEPREFIX'],
|
||||
'drive_c/Qt'))
|
||||
else:
|
||||
homeQt = os.path.expanduser('~/.wine/drive_c/Qt')
|
||||
else:
|
||||
homeQt = os.path.expanduser('~/Qt')
|
||||
|
||||
binCreator = 'binarycreator'
|
||||
|
||||
if self.targetSystem == 'windows' or self.targetSystem == 'posix_windows':
|
||||
binCreator += '.exe'
|
||||
|
||||
for root, _, files in os.walk(homeQt):
|
||||
for f in files:
|
||||
if f == binCreator:
|
||||
self.qtIFW = os.path.join(root, f)
|
||||
|
||||
return
|
||||
|
||||
# binarycreator offered by the system is most probably dynamically
|
||||
# linked, so it's useful for test purposes only, but not recommended
|
||||
# for distribution.
|
||||
self.qtIFW = self.whereBin(binCreator)
|
||||
|
||||
def detectQtIFWVersion(self):
|
||||
self.qtIFWVersion = ''
|
||||
|
||||
if self.qtIFW == '':
|
||||
return
|
||||
|
||||
installerBase = os.path.join(os.path.dirname(self.qtIFW),
|
||||
'installerbase')
|
||||
|
||||
if self.targetSystem == 'windows' or self.targetSystem == 'posix_windows':
|
||||
installerBase += '.exe'
|
||||
|
||||
self.qtIFWVersion = '2.0.0'
|
||||
|
||||
if not os.path.exists(installerBase):
|
||||
return
|
||||
|
||||
if self.targetSystem == 'posix_windows':
|
||||
installerBase = 'Z:' + installerBase.replace('/', '\\')
|
||||
process = subprocess.Popen(['wine', # nosec
|
||||
installerBase,
|
||||
'--version'],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, _ = process.communicate(input=b'\n')
|
||||
else:
|
||||
process = subprocess.Popen([installerBase, # nosec
|
||||
'--version'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, _ = process.communicate()
|
||||
|
||||
for line in stdout.split(b'\n'):
|
||||
if b'IFW Version:' in line:
|
||||
self.qtIFWVersion = line.split(b' ')[2].replace(b'"', b'').replace(b',', b'').decode(sys.getdefaultencoding())
|
||||
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def listQmlFiles(path):
|
||||
qmlFiles = set()
|
||||
|
||||
if os.path.isfile(path):
|
||||
baseName = os.path.basename(path)
|
||||
|
||||
if baseName == 'qmldir' or path.endswith('.qml'):
|
||||
qmlFiles.add(path)
|
||||
else:
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
if f == 'qmldir' or f.endswith('.qml'):
|
||||
qmlFiles.add(os.path.join(root, f))
|
||||
|
||||
return list(qmlFiles)
|
||||
|
||||
@staticmethod
|
||||
def modulePath(importLine):
|
||||
imp = importLine.strip().split()
|
||||
path = imp[1].replace('.', '/')
|
||||
majorVersion = imp[2].split('.')[0]
|
||||
|
||||
if int(majorVersion) > 1:
|
||||
path += '.{}'.format(majorVersion)
|
||||
|
||||
return path
|
||||
|
||||
def scanImports(self, path):
|
||||
if not os.path.isfile(path):
|
||||
return []
|
||||
|
||||
fileName = os.path.basename(path)
|
||||
imports = set()
|
||||
|
||||
if fileName.endswith('.qml'):
|
||||
with open(path, 'rb') as f:
|
||||
for line in f:
|
||||
if re.match(b'^import \\w+' , line):
|
||||
imports.add(self.modulePath(line.strip().decode(sys.getdefaultencoding())))
|
||||
elif fileName == 'qmldir':
|
||||
with open(path, 'rb') as f:
|
||||
for line in f:
|
||||
if re.match(b'^depends ' , line):
|
||||
imports.add(self.modulePath(line.strip().decode(sys.getdefaultencoding())))
|
||||
|
||||
return list(imports)
|
||||
|
||||
def solvedepsQml(self):
|
||||
qmlFiles = set()
|
||||
|
||||
for path in self.qmlRootDirs:
|
||||
path = os.path.join(self.rootDir, path)
|
||||
|
||||
for f in self.listQmlFiles(path):
|
||||
qmlFiles.add(f)
|
||||
|
||||
solved = set()
|
||||
solvedImports = set()
|
||||
|
||||
while len(qmlFiles) > 0:
|
||||
qmlFile = qmlFiles.pop()
|
||||
|
||||
for imp in self.scanImports(qmlFile):
|
||||
if imp in solvedImports:
|
||||
continue
|
||||
|
||||
sysModulePath = os.path.join(self.qtInstallQml, imp)
|
||||
installModulePath = os.path.join(self.qmlInstallDir, imp)
|
||||
|
||||
if os.path.exists(sysModulePath):
|
||||
print(' {} -> {}'.format(sysModulePath, installModulePath))
|
||||
self.copy(sysModulePath, installModulePath)
|
||||
solvedImports.add(imp)
|
||||
self.dependencies.append(os.path.join(sysModulePath, 'qmldir'))
|
||||
|
||||
for f in self.listQmlFiles(sysModulePath):
|
||||
if not f in solved:
|
||||
qmlFiles.add(f)
|
||||
|
||||
solved.add(qmlFile)
|
||||
|
||||
def solvedepsPlugins(self):
|
||||
pluginsMap = {
|
||||
'Qt53DRenderer': ['sceneparsers', 'geometryloaders'],
|
||||
'Qt53DQuickRenderer': ['renderplugins'],
|
||||
'Qt5Declarative': ['qml1tooling'],
|
||||
'Qt5EglFSDeviceIntegration': ['egldeviceintegrations'],
|
||||
'Qt5Gui': ['accessible',
|
||||
'generic',
|
||||
'iconengines',
|
||||
'imageformats',
|
||||
'platforms',
|
||||
'platforminputcontexts',
|
||||
'styles'],
|
||||
'Qt5Location': ['geoservices'],
|
||||
'Qt5Multimedia': ['audio', 'mediaservice', 'playlistformats'],
|
||||
'Qt5Network': ['bearer'],
|
||||
'Qt5Positioning': ['position'],
|
||||
'Qt5PrintSupport': ['printsupport'],
|
||||
'Qt5QmlTooling': ['qmltooling'],
|
||||
'Qt5Quick': ['scenegraph', 'qmltooling'],
|
||||
'Qt5Sensors': ['sensors', 'sensorgestures'],
|
||||
'Qt5SerialBus': ['canbus'],
|
||||
'Qt5Sql': ['sqldrivers'],
|
||||
'Qt5TextToSpeech': ['texttospeech'],
|
||||
'Qt5WebEngine': ['qtwebengine'],
|
||||
'Qt5WebEngineCore': ['qtwebengine'],
|
||||
'Qt5WebEngineWidgets': ['qtwebengine'],
|
||||
'Qt5WebView': ['webview'],
|
||||
'Qt5XcbQpa': ['xcbglintegrations']
|
||||
}
|
||||
|
||||
pluginsMap.update({lib + 'd': pluginsMap[lib] for lib in pluginsMap})
|
||||
|
||||
if self.targetSystem == 'android':
|
||||
pluginsMap.update({lib + '_' + self.targetArch: pluginsMap[lib] for lib in pluginsMap})
|
||||
|
||||
plugins = []
|
||||
|
||||
for dep in self.binarySolver.scanDependencies(self.installDir):
|
||||
libName = self.binarySolver.name(dep)
|
||||
|
||||
if not libName in pluginsMap:
|
||||
continue
|
||||
|
||||
for plugin in pluginsMap[libName]:
|
||||
if not plugin in plugins:
|
||||
sysPluginPath = os.path.join(self.qtInstallPlugins, plugin)
|
||||
pluginPath = os.path.join(self.pluginsInstallDir, plugin)
|
||||
|
||||
if not os.path.exists(sysPluginPath):
|
||||
continue
|
||||
|
||||
print(' {} -> {}'.format(sysPluginPath, pluginPath))
|
||||
self.copy(sysPluginPath, pluginPath)
|
||||
plugins.append(plugin)
|
||||
self.dependencies.append(sysPluginPath)
|
||||
|
||||
def solvedepsAndroid(self):
|
||||
installPrefix = self.qmakeQuery(var='QT_INSTALL_PREFIX')
|
||||
qtLibsPath = self.qmakeQuery(var='QT_INSTALL_LIBS')
|
||||
jars = []
|
||||
permissions = set()
|
||||
features = set()
|
||||
initClasses = set()
|
||||
libs = set()
|
||||
|
||||
for f in os.listdir(self.libInstallDir):
|
||||
basename = os.path.basename(f)[3:]
|
||||
basename = os.path.splitext(basename)[0]
|
||||
depFile = os.path.join(qtLibsPath,
|
||||
basename + '-android-dependencies.xml')
|
||||
|
||||
if os.path.exists(depFile):
|
||||
tree = ET.parse(depFile)
|
||||
root = tree.getroot()
|
||||
|
||||
for jar in root.iter('jar'):
|
||||
jars.append(jar.attrib['file'])
|
||||
|
||||
if 'initClass' in jar.attrib:
|
||||
initClasses.append(jar.attrib['initClass'])
|
||||
|
||||
for permission in root.iter('permission'):
|
||||
permissions.add(permission.attrib['name'])
|
||||
|
||||
for feature in root.iter('feature'):
|
||||
features.add(feature.attrib['name'])
|
||||
|
||||
for lib in root.iter('lib'):
|
||||
if 'file' in lib.attrib:
|
||||
libs.add(lib.attrib['file'])
|
||||
|
||||
self.localLibs = [os.path.basename(lib) for lib in libs]
|
||||
|
||||
print('Copying jar files\n')
|
||||
|
||||
for jar in sorted(jars):
|
||||
srcPath = os.path.join(installPrefix, jar)
|
||||
dstPath = os.path.join(self.rootInstallDir,
|
||||
'libs',
|
||||
os.path.basename(jar))
|
||||
print(' {} -> {}'.format(srcPath, dstPath))
|
||||
self.copy(srcPath, dstPath)
|
||||
|
||||
manifest = os.path.join(self.rootInstallDir, 'AndroidManifest.xml')
|
||||
manifestTemp = os.path.join(self.rootInstallDir, 'AndroidManifestTemp.xml')
|
||||
tree = ET.parse(manifest)
|
||||
root = tree.getroot()
|
||||
oldFeatures = set()
|
||||
oldPermissions = set()
|
||||
|
||||
for element in root:
|
||||
if element.tag == 'uses-feature':
|
||||
for key in element.attrib:
|
||||
if key.endswith('name'):
|
||||
oldFeatures.add(element.attrib[key])
|
||||
elif element.tag == 'uses-permission':
|
||||
for key in element.attrib:
|
||||
if key.endswith('name'):
|
||||
oldPermissions.add(element.attrib[key])
|
||||
|
||||
features -= oldFeatures
|
||||
permissions -= oldPermissions
|
||||
featuresWritten = len(features) < 1
|
||||
permissionsWritten = len(permissions) < 1
|
||||
replace = {'-- %%INSERT_INIT_CLASSES%% --' : ':'.join(sorted(initClasses)),
|
||||
'-- %%BUNDLE_LOCAL_QT_LIBS%% --': '1',
|
||||
'-- %%USE_LOCAL_QT_LIBS%% --' : '1',
|
||||
'-- %%INSERT_LOCAL_LIBS%% --' : ':'.join(sorted(libs)),
|
||||
'-- %%INSERT_LOCAL_JARS%% --' : ':'.join(sorted(jars))}
|
||||
|
||||
with open(manifest) as inFile:
|
||||
with open(manifestTemp, 'w') as outFile:
|
||||
for line in inFile:
|
||||
for key in replace:
|
||||
line = line.replace(key, replace[key])
|
||||
|
||||
outFile.write(line)
|
||||
spaces = len(line)
|
||||
line = line.lstrip()
|
||||
spaces -= len(line)
|
||||
|
||||
if line.startswith('<uses-feature') and not featuresWritten:
|
||||
print('\nUpdating features\n')
|
||||
|
||||
for feature in features:
|
||||
print(' ' + feature)
|
||||
outFile.write(spaces * ' ' + '<uses-feature android:name="{}"/>\n'.format(feature))
|
||||
|
||||
featuresWritten = True
|
||||
|
||||
if line.startswith('<uses-permission') and not permissionsWritten:
|
||||
print('\nUpdating permissions\n')
|
||||
|
||||
for permission in permissions:
|
||||
print(' ' + permission)
|
||||
outFile.write(spaces * ' ' + '<uses-permission android:name="{}"/>\n'.format(permission))
|
||||
|
||||
permissionsWritten = True
|
||||
|
||||
os.remove(manifest)
|
||||
shutil.move(manifestTemp, manifest)
|
||||
|
||||
def writeQtConf(self):
|
||||
prefix = self.binaryInstallDir
|
||||
|
||||
if self.targetSystem == 'mac':
|
||||
prefix = os.path.abspath(os.path.join(self.binaryInstallDir, '..'))
|
||||
|
||||
paths = {'Plugins': os.path.relpath(self.pluginsInstallDir, prefix).replace('\\', '/'),
|
||||
'Imports': os.path.relpath(self.qmlInstallDir, prefix).replace('\\', '/'),
|
||||
'Qml2Imports': os.path.relpath(self.qmlInstallDir, prefix).replace('\\', '/')}
|
||||
confPath = os.path.dirname(self.qtConf)
|
||||
|
||||
if not os.path.exists(confPath):
|
||||
os.makedirs(confPath)
|
||||
|
||||
with open(self.qtConf, 'w') as qtconf:
|
||||
qtconf.write('[Paths]\n')
|
||||
|
||||
for path in paths:
|
||||
qtconf.write('{} = {}\n'.format(path, paths[path]))
|
||||
|
||||
@staticmethod
|
||||
def readChangeLog(changeLog, appName, version):
|
||||
if os.path.exists(changeLog):
|
||||
with open(changeLog) as f:
|
||||
for line in f:
|
||||
if not line.startswith('{0} {1}:'.format(appName, version)):
|
||||
continue
|
||||
|
||||
# Skip first line.
|
||||
f.readline()
|
||||
changeLogText = ''
|
||||
|
||||
for line_ in f:
|
||||
if re.match('{} \d+\.\d+\.\d+:'.format(appName), line):
|
||||
# Remove last line.
|
||||
i = changeLogText.rfind('\n')
|
||||
|
||||
if i >= 0:
|
||||
changeLogText = changeLogText[: i]
|
||||
|
||||
return changeLogText
|
||||
|
||||
changeLogText += line_
|
||||
|
||||
return ''
|
||||
|
||||
def createInstaller(self):
|
||||
if not os.path.exists(self.qtIFW):
|
||||
return False
|
||||
|
||||
# Read package config
|
||||
packageConf = configparser.ConfigParser()
|
||||
packageConf.optionxform=str
|
||||
packageConf.read(self.packageConfig, 'utf-8')
|
||||
|
||||
# Create layout
|
||||
componentName = 'com.{0}prj.{0}'.format(self.programName)
|
||||
packageDir = os.path.join(self.installerPackages, componentName)
|
||||
|
||||
if not os.path.exists(self.installerConfig):
|
||||
os.makedirs(self.installerConfig)
|
||||
|
||||
dataDir = os.path.join(packageDir, 'data')
|
||||
metaDir = os.path.join(packageDir, 'meta')
|
||||
|
||||
if not os.path.exists(dataDir):
|
||||
os.makedirs(dataDir)
|
||||
|
||||
if not os.path.exists(metaDir):
|
||||
os.makedirs(metaDir)
|
||||
|
||||
iconName = ''
|
||||
|
||||
if self.appIcon != '' and os.path.exists(self.appIcon):
|
||||
self.copy(self.appIcon, self.installerConfig)
|
||||
iconName = os.path.splitext(os.path.basename(self.appIcon))[0]
|
||||
|
||||
licenseOutFile = os.path.basename(self.licenseFile)
|
||||
|
||||
if not '.' in licenseOutFile and \
|
||||
(self.targetSystem == 'windows' or \
|
||||
self.targetSystem == 'posix_windows'):
|
||||
licenseOutFile += '.txt'
|
||||
|
||||
self.copy(self.licenseFile, os.path.join(metaDir, licenseOutFile))
|
||||
self.copy(self.rootInstallDir, dataDir)
|
||||
|
||||
configXml = os.path.join(self.installerConfig, 'config.xml')
|
||||
appName = packageConf['Package']['appName'].strip()
|
||||
|
||||
with open(configXml, 'w') as config:
|
||||
config.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||
config.write('<Installer>\n')
|
||||
config.write(' <Name>{}</Name>\n'.format(appName))
|
||||
|
||||
if 'DAILY_BUILD' in os.environ:
|
||||
config.write(' <Version>0.0.0</Version>\n')
|
||||
else:
|
||||
config.write(' <Version>{}</Version>\n'.format(self.programVersion))
|
||||
|
||||
config.write(' <Title>{}</Title>\n'.format(packageConf['Package']['description'].strip()))
|
||||
config.write(' <Publisher>{}</Publisher>\n'.format(appName))
|
||||
config.write(' <ProductUrl>{}</ProductUrl>\n'.format(packageConf['Package']['url'].strip()))
|
||||
|
||||
if iconName != '':
|
||||
config.write(' <InstallerWindowIcon>{}</InstallerWindowIcon>\n'.format(iconName))
|
||||
config.write(' <InstallerApplicationIcon>{}</InstallerApplicationIcon>\n'.format(iconName))
|
||||
config.write(' <Logo>{}</Logo>\n'.format(iconName))
|
||||
|
||||
if self.installerRunProgram != '':
|
||||
config.write(' <RunProgram>{}</RunProgram>\n'.format(self.installerRunProgram))
|
||||
config.write(' <RunProgramDescription>{}</RunProgramDescription>\n'.format(packageConf['Package']['runMessage'].strip()))
|
||||
config.write(' <StartMenuDir>{}</StartMenuDir>\n'.format(appName))
|
||||
|
||||
config.write(' <MaintenanceToolName>{}Uninstall</MaintenanceToolName>\n'.format(appName))
|
||||
config.write(' <AllowNonAsciiCharacters>true</AllowNonAsciiCharacters>\n')
|
||||
config.write(' <TargetDir>{}</TargetDir>\n'.format(self.installerTargetDir))
|
||||
config.write('</Installer>\n')
|
||||
|
||||
self.copy(self.installerScript,
|
||||
os.path.join(metaDir, 'installscript.qs'))
|
||||
|
||||
with open(os.path.join(metaDir, 'package.xml'), 'w') as f:
|
||||
f.write('<?xml version="1.0"?>\n')
|
||||
f.write('<Package>\n')
|
||||
f.write(' <DisplayName>{}</DisplayName>\n'.format(appName))
|
||||
f.write(' <Description>{}</Description>\n'.format(packageConf['Package']['description'].strip()))
|
||||
|
||||
if 'DAILY_BUILD' in os.environ:
|
||||
f.write(' <Version>0.0.0</Version>\n')
|
||||
else:
|
||||
f.write(' <Version>{}</Version>\n'.format(self.programVersion))
|
||||
|
||||
f.write(' <ReleaseDate>{}</ReleaseDate>\n'.format(time.strftime('%Y-%m-%d')))
|
||||
f.write(' <Name>{}</Name>\n'.format(componentName))
|
||||
f.write(' <Licenses>\n')
|
||||
f.write(' <License name="{0}" file="{1}" />\n'.format(packageConf['Package']['licenseDescription'].strip(),
|
||||
licenseOutFile))
|
||||
f.write(' </Licenses>\n')
|
||||
f.write(' <Script>installscript.qs</Script>\n')
|
||||
f.write(' <UpdateText>\n')
|
||||
|
||||
if not 'DAILY_BUILD' in os.environ:
|
||||
f.write(self.readChangeLog(self.changeLog,
|
||||
appName,
|
||||
self.programVersion))
|
||||
|
||||
f.write(' </UpdateText>\n')
|
||||
f.write(' <Default>true</Default>\n')
|
||||
f.write(' <ForcedInstallation>true</ForcedInstallation>\n')
|
||||
f.write(' <Essential>false</Essential>\n')
|
||||
|
||||
if self.adminRights:
|
||||
f.write(' <RequiresAdminRights>true</RequiresAdminRights>\n')
|
||||
|
||||
f.write('</Package>\n')
|
||||
|
||||
# Remove old file
|
||||
if not os.path.exists(self.pkgsDir):
|
||||
os.makedirs(self.pkgsDir)
|
||||
|
||||
if os.path.exists(self.outPackage):
|
||||
os.remove(self.outPackage)
|
||||
|
||||
params = []
|
||||
|
||||
if self.targetSystem == 'posix_windows':
|
||||
params = ['wine']
|
||||
|
||||
params += [self.qtIFW,
|
||||
'-c', configXml,
|
||||
'-p', self.installerPackages,
|
||||
self.outPackage]
|
||||
process = subprocess.Popen(params, # nosec
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
process.communicate()
|
||||
|
||||
return True
|
||||
|
||||
def copyAndroidTemplates(self):
|
||||
installPrefix = self.qmakeQuery(var='QT_INSTALL_PREFIX')
|
||||
sourcesPath = os.path.join(installPrefix, 'src')
|
||||
templates = [os.path.join(sourcesPath, '3rdparty/gradle'),
|
||||
os.path.join(sourcesPath, 'android/templates')]
|
||||
|
||||
for template in templates:
|
||||
self.copy(template, self.rootInstallDir, overwrite=False)
|
||||
|
||||
deploymentSettingsPath = ''
|
||||
|
||||
for f in os.listdir(self.standAloneDir):
|
||||
if re.match('^android-.+-deployment-settings.json$' , f):
|
||||
deploymentSettingsPath = os.path.join(self.standAloneDir, f)
|
||||
|
||||
break
|
||||
|
||||
if len(deploymentSettingsPath) < 1:
|
||||
return
|
||||
|
||||
with open(deploymentSettingsPath) as f:
|
||||
deploymentSettings = json.load(f)
|
||||
|
||||
properties = os.path.join(self.rootInstallDir, 'gradle.properties')
|
||||
platform = self.androidPlatform.replace('android-', '')
|
||||
javaDir = os.path.join(sourcesPath, 'android','java')
|
||||
|
||||
with open(properties, 'w') as f:
|
||||
if 'sdkBuildToolsRevision' in deploymentSettings:
|
||||
f.write('androidBuildToolsVersion={}\n'.format(deploymentSettings['sdkBuildToolsRevision']))
|
||||
|
||||
f.write('androidCompileSdkVersion={}\n'.format(platform))
|
||||
f.write('buildDir=build\n')
|
||||
f.write('qt5AndroidDir={}\n'.format(javaDir))
|
||||
|
||||
def createRccBundle(self):
|
||||
rcc = os.path.join(os.path.dirname(self.qmake), 'rcc')
|
||||
assetsDir = os.path.abspath(os.path.join(self.assetsIntallDir, '..'))
|
||||
assetsFolder = os.path.relpath(self.assetsIntallDir, assetsDir)
|
||||
qrcFile = os.path.join(self.assetsIntallDir, assetsFolder + '.qrc')
|
||||
|
||||
params = [rcc,
|
||||
'--project',
|
||||
'-o', qrcFile]
|
||||
process = subprocess.Popen(params, # nosec
|
||||
cwd=self.assetsIntallDir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
process.communicate()
|
||||
|
||||
params = [rcc,
|
||||
'--root=/{}'.format(assetsFolder),
|
||||
'--binary',
|
||||
'-o', self.assetsIntallDir + '.rcc',
|
||||
qrcFile]
|
||||
process = subprocess.Popen(params, # nosec
|
||||
cwd=assetsDir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
process.communicate()
|
||||
|
||||
shutil.rmtree(self.assetsIntallDir, True)
|
|
@ -1,255 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Webcamoid, webcam capture application.
|
||||
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||||
#
|
||||
# Webcamoid is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Webcamoid 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
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Web-Site: http://webcamoid.github.io/
|
||||
|
||||
import fnmatch
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess # nosec
|
||||
import sys
|
||||
|
||||
class DeployToolsUtils:
|
||||
def __init__(self):
|
||||
self.make = ''
|
||||
|
||||
if os.name == 'posix' and sys.platform.startswith('darwin'):
|
||||
self.system = 'mac'
|
||||
elif os.name == 'nt' and sys.platform.startswith('win32'):
|
||||
self.system = 'windows'
|
||||
elif os.name == 'posix':
|
||||
self.system = 'posix'
|
||||
else:
|
||||
self.system = ''
|
||||
|
||||
self.arch = platform.architecture()[0]
|
||||
self.targetArch = self.arch
|
||||
self.targetSystem = self.system
|
||||
pathSep = ';' if self.system == 'windows' else ':'
|
||||
self.sysBinsPath = []
|
||||
|
||||
if 'PATH' in os.environ:
|
||||
self.sysBinsPath += \
|
||||
[path.strip() for path in os.environ['PATH'].split(pathSep)]
|
||||
|
||||
self.njobs = multiprocessing.cpu_count()
|
||||
|
||||
if self.njobs < 4:
|
||||
self.njobs = 4
|
||||
|
||||
def detectTargetArch(self, binary=''):
|
||||
if binary == '':
|
||||
binary = self.mainBinary
|
||||
|
||||
self.targetArch = platform.architecture(binary)[0]
|
||||
|
||||
def whereBin(self, binary):
|
||||
for path in self.sysBinsPath:
|
||||
path = os.path.join(path, binary)
|
||||
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
return ''
|
||||
|
||||
def copy(self, src, dst='.', copyReals=False, overwrite=True):
|
||||
if not os.path.exists(src):
|
||||
return False
|
||||
|
||||
if os.path.isdir(src):
|
||||
if os.path.isfile(dst):
|
||||
return False
|
||||
|
||||
for root, dirs, files in os.walk(src):
|
||||
for f in files:
|
||||
fromF = os.path.join(root, f)
|
||||
toF = os.path.relpath(fromF, src)
|
||||
toF = os.path.join(dst, toF)
|
||||
toF = os.path.normpath(toF)
|
||||
self.copy(fromF, toF, copyReals, overwrite)
|
||||
|
||||
for d in dirs:
|
||||
fromD = os.path.join(root, d)
|
||||
toD = os.path.relpath(fromD, src)
|
||||
toD = os.path.join(dst, toD)
|
||||
|
||||
try:
|
||||
os.makedirs(os.path.normpath(toD))
|
||||
except:
|
||||
pass
|
||||
elif os.path.isfile(src):
|
||||
if os.path.isdir(dst):
|
||||
dst = os.path.realpath(dst)
|
||||
dst = os.path.join(dst, os.path.basename(src))
|
||||
|
||||
dirname = os.path.dirname(dst)
|
||||
|
||||
if not os.path.exists(dirname):
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except:
|
||||
return False
|
||||
|
||||
if os.path.exists(dst):
|
||||
if not overwrite:
|
||||
return True
|
||||
|
||||
try:
|
||||
os.remove(dst)
|
||||
except:
|
||||
return False
|
||||
|
||||
if copyReals and os.path.islink(src):
|
||||
realpath = os.path.realpath(src)
|
||||
basename = os.path.basename(realpath)
|
||||
os.symlink(os.path.join('.', basename), dst)
|
||||
self.copy(realpath,
|
||||
os.path.join(dirname, basename),
|
||||
copyReals,
|
||||
overwrite)
|
||||
else:
|
||||
try:
|
||||
if self.system == 'windows':
|
||||
shutil.copy(src, dst)
|
||||
else:
|
||||
shutil.copy(src, dst, follow_symlinks=False)
|
||||
except:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def move(self, src, dst='.', moveReals=False):
|
||||
if not os.path.exists(src):
|
||||
return False
|
||||
|
||||
if os.path.isdir(src):
|
||||
if os.path.isfile(dst):
|
||||
return False
|
||||
|
||||
for root, dirs, files in os.walk(src):
|
||||
for f in files:
|
||||
fromF = os.path.join(root, f)
|
||||
toF = os.path.relpath(fromF, src)
|
||||
toF = os.path.join(dst, toF)
|
||||
toF = os.path.normpath(toF)
|
||||
self.move(fromF, toF, moveReals)
|
||||
|
||||
for d in dirs:
|
||||
fromD = os.path.join(root, d)
|
||||
toD = os.path.relpath(fromD, src)
|
||||
toD = os.path.join(dst, toD)
|
||||
|
||||
try:
|
||||
os.makedirs(os.path.normpath(toD))
|
||||
except:
|
||||
pass
|
||||
elif os.path.isfile(src):
|
||||
if os.path.isdir(dst):
|
||||
dst = os.path.realpath(dst)
|
||||
dst = os.path.join(dst, os.path.basename(src))
|
||||
|
||||
dirname = os.path.dirname(dst)
|
||||
|
||||
if not os.path.exists(dirname):
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except:
|
||||
return False
|
||||
|
||||
if os.path.exists(dst):
|
||||
try:
|
||||
os.remove(dst)
|
||||
except:
|
||||
return False
|
||||
|
||||
if moveReals and os.path.islink(src):
|
||||
realpath = os.path.realpath(src)
|
||||
basename = os.path.basename(realpath)
|
||||
os.symlink(os.path.join('.', basename), dst)
|
||||
self.move(realpath, os.path.join(dirname, basename), moveReals)
|
||||
else:
|
||||
try:
|
||||
shutil.move(src, dst)
|
||||
except:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def detectMake(self):
|
||||
if 'MAKE_PATH' in os.environ:
|
||||
self.make = os.environ['MAKE_PATH']
|
||||
|
||||
return
|
||||
|
||||
makes = ['mingw32-make', 'make'] if self.system == 'windows' else ['make']
|
||||
ext = '.exe' if self.system == 'windows' else ''
|
||||
|
||||
for make in makes:
|
||||
makePath = self.whereBin(make + ext)
|
||||
|
||||
if makePath != '':
|
||||
self.make = makePath
|
||||
|
||||
break
|
||||
|
||||
def makeInstall(self, buildDir, params={}):
|
||||
previousDir = os.getcwd()
|
||||
os.chdir(buildDir)
|
||||
params_ = [key + '=' + params[key] for key in params]
|
||||
process = subprocess.Popen([self.make, 'install'] + params_, # nosec
|
||||
stdout=subprocess.PIPE)
|
||||
|
||||
process.communicate()
|
||||
os.chdir(previousDir)
|
||||
|
||||
return process.returncode
|
||||
|
||||
@staticmethod
|
||||
def sha256sum(fileName):
|
||||
sha = hashlib.sha256()
|
||||
|
||||
with open(fileName, 'rb') as f:
|
||||
while True:
|
||||
data = f.read(1024)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
sha.update(data)
|
||||
|
||||
return sha.hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def detectMakeFiles(makePath):
|
||||
makeFiles = []
|
||||
|
||||
try:
|
||||
for f in os.listdir(makePath):
|
||||
path = os.path.join(makePath, f)
|
||||
|
||||
if os.path.isfile(path) and fnmatch.fnmatch(f.lower(), 'makefile*'):
|
||||
makeFiles += [path]
|
||||
except:
|
||||
pass
|
||||
|
||||
return makeFiles
|
Loading…
Reference in a new issue