Adapt build/package scripts to single-edition

This commit is contained in:
Virgil Dupras 2016-05-31 20:21:07 -04:00
parent c865f84c16
commit ad45a6e16e
6 changed files with 128 additions and 291 deletions

View File

@ -5,9 +5,6 @@ a system. It's written mostly in Python 3 and has the peculiarity of using
[multiple GUI toolkits][cross-toolkit], all using the same core Python code. On OS X, the UI layer [multiple GUI toolkits][cross-toolkit], all using the same core Python code. On OS X, the UI layer
is written in Objective-C and uses Cocoa. On Linux, it's written in Python and uses Qt5. is written in Objective-C and uses Cocoa. On Linux, it's written in Python and uses Qt5.
dupeGuru comes in 3 editions (standard, music and picture) which are all buildable from this same
source tree. You choose the edition you want to build in a `configure.py` flag.
# Current status: People wanted # Current status: People wanted
dupeGuru has currently only one maintainer, me. This is a dangerous situation that needs to be dupeGuru has currently only one maintainer, me. This is a dangerous situation that needs to be
@ -121,7 +118,6 @@ For OS X:
With your virtualenv activated, you can build and run dupeGuru with these commands: With your virtualenv activated, you can build and run dupeGuru with these commands:
$ python configure.py
$ python build.py $ python build.py
$ python run.py $ python run.py

View File

@ -44,4 +44,4 @@ else
fi fi
echo "Bootstrapping complete! You can now configure, build and run dupeGuru with:" echo "Bootstrapping complete! You can now configure, build and run dupeGuru with:"
echo ". env/bin/activate && python configure.py && python build.py && python run.py" echo ". env/bin/activate && python build.py && python run.py"

164
build.py
View File

@ -1,6 +1,4 @@
# Created By: Virgil Dupras # Copyright 2016 Hardcoded Software (http://www.hardcoded.net)
# Created On: 2009-12-30
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
# #
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, # This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
@ -11,7 +9,6 @@ import os
import os.path as op import os.path as op
from optparse import OptionParser from optparse import OptionParser
import shutil import shutil
import json
import importlib import importlib
import compileall import compileall
@ -22,10 +19,10 @@ from hscommon.build import (
add_to_pythonpath, print_and_do, copy_packages, filereplace, add_to_pythonpath, print_and_do, copy_packages, filereplace,
get_module_version, move_all, copy_all, OSXAppStructure, get_module_version, move_all, copy_all, OSXAppStructure,
build_cocoalib_xibless, fix_qt_resource_file, build_cocoa_ext, copy_embeddable_python_dylib, build_cocoalib_xibless, fix_qt_resource_file, build_cocoa_ext, copy_embeddable_python_dylib,
collect_stdlib_dependencies, copy collect_stdlib_dependencies
) )
from hscommon import loc from hscommon import loc
from hscommon.plat import ISOSX, ISLINUX from hscommon.plat import ISOSX
from hscommon.util import ensure_folder, delete_files_with_pattern from hscommon.util import ensure_folder, delete_files_with_pattern
def parse_args(): def parse_args():
@ -39,6 +36,14 @@ def parse_args():
'--doc', action='store_true', dest='doc', '--doc', action='store_true', dest='doc',
help="Build only the help file" help="Build only the help file"
) )
parser.add_option(
'--ui', dest='ui',
help="Type of UI to build. 'qt' or 'cocoa'. Default is determined by your system."
)
parser.add_option(
'--dev', action='store_true', dest='dev', default=False,
help="If this flag is set, will configure for dev builds."
)
parser.add_option( parser.add_option(
'--loc', action='store_true', dest='loc', '--loc', action='store_true', dest='loc',
help="Build only localization" help="Build only localization"
@ -70,15 +75,11 @@ def parse_args():
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
return options return options
def cocoa_app(edition): def cocoa_app():
app_path = { app_path = 'build/dupeGuru.app'
'se': 'build/dupeGuru.app',
'me': 'build/dupeGuru ME.app',
'pe': 'build/dupeGuru PE.app',
}[edition]
return OSXAppStructure(app_path) return OSXAppStructure(app_path)
def build_xibless(edition, dest='cocoa/autogen'): def build_xibless(dest='cocoa/autogen'):
import xibless import xibless
ensure_folder(dest) ensure_folder(dest)
FNPAIRS = [ FNPAIRS = [
@ -94,35 +95,36 @@ def build_xibless(edition, dest='cocoa/autogen'):
for srcname, dstname in FNPAIRS: for srcname, dstname in FNPAIRS:
xibless.generate( xibless.generate(
op.join('cocoa', 'base', 'ui', srcname), op.join(dest, dstname), op.join('cocoa', 'base', 'ui', srcname), op.join(dest, dstname),
localizationTable='Localizable', args={'edition': edition}
)
if edition == 'pe':
xibless.generate(
'cocoa/pe/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'),
localizationTable='Localizable'
)
else:
xibless.generate(
'cocoa/base/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'),
localizationTable='Localizable' localizationTable='Localizable'
) )
# XXX This is broken
assert False
# if edition == 'pe':
# xibless.generate(
# 'cocoa/pe/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'),
# localizationTable='Localizable'
# )
# else:
# xibless.generate(
# 'cocoa/base/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'),
# localizationTable='Localizable'
# )
def build_cocoa(edition, dev): def build_cocoa(dev):
print("Creating OS X app structure") print("Creating OS X app structure")
ed = lambda s: s.format(edition) app = cocoa_app()
app = cocoa_app(edition) app_version = get_module_version('core_se')
app_version = get_module_version(ed('core_{}')) cocoa_project_path = 'cocoa/se'
cocoa_project_path = ed('cocoa/{}')
filereplace(op.join(cocoa_project_path, 'InfoTemplate.plist'), op.join('build', 'Info.plist'), version=app_version) filereplace(op.join(cocoa_project_path, 'InfoTemplate.plist'), op.join('build', 'Info.plist'), version=app_version)
app.create(op.join('build', 'Info.plist')) app.create(op.join('build', 'Info.plist'))
print("Building localizations") print("Building localizations")
build_localizations('cocoa', edition) build_localizations('cocoa')
print("Building xibless UIs") print("Building xibless UIs")
build_cocoalib_xibless() build_cocoalib_xibless()
build_xibless(edition) build_xibless()
print("Building Python extensions") print("Building Python extensions")
build_cocoa_proxy_module() build_cocoa_proxy_module()
build_cocoa_bridging_interfaces(edition) build_cocoa_bridging_interfaces()
print("Building the cocoa layer") print("Building the cocoa layer")
copy_embeddable_python_dylib('build') copy_embeddable_python_dylib('build')
pydep_folder = op.join(app.resources, 'py') pydep_folder = op.join(app.resources, 'py')
@ -130,21 +132,15 @@ def build_cocoa(edition, dev):
os.mkdir(pydep_folder) os.mkdir(pydep_folder)
shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build') shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build')
appscript_pkgs = ['appscript', 'aem', 'mactypes', 'osax'] appscript_pkgs = ['appscript', 'aem', 'mactypes', 'osax']
specific_packages = {
'se': ['core_se'],
'me': ['core_me'] + appscript_pkgs + ['hsaudiotag'],
'pe': ['core_pe'] + appscript_pkgs,
}[edition]
tocopy = [ tocopy = [
'core', 'hscommon', 'cocoa/inter', 'cocoalib/cocoa', 'objp', 'send2trash' 'core', 'core_se', 'core_me', 'core_pe', 'hscommon', 'cocoa/inter', 'cocoalib/cocoa',
] + specific_packages 'objp', 'send2trash', 'hsaudiotag',
] + appscript_pkgs
copy_packages(tocopy, pydep_folder, create_links=dev) copy_packages(tocopy, pydep_folder, create_links=dev)
sys.path.insert(0, 'build') sys.path.insert(0, 'build')
extra_deps = None # ModuleFinder can't seem to correctly detect the multiprocessing dependency, so we have
if edition == 'pe': # to manually specify it.
# ModuleFinder can't seem to correctly detect the multiprocessing dependency, so we have extra_deps = ['multiprocessing']
# to manually specify it.
extra_deps = ['multiprocessing']
collect_stdlib_dependencies('build/dg_cocoa.py', pydep_folder, extra_deps=extra_deps) collect_stdlib_dependencies('build/dg_cocoa.py', pydep_folder, extra_deps=extra_deps)
del sys.path[0] del sys.path[0]
# Views are not referenced by python code, so they're not found by the collector. # Views are not referenced by python code, so they're not found by the collector.
@ -157,12 +153,12 @@ def build_cocoa(edition, dev):
delete_files_with_pattern(pydep_folder, '__pycache__') delete_files_with_pattern(pydep_folder, '__pycache__')
print("Compiling with WAF") print("Compiling with WAF")
os.chdir('cocoa') os.chdir('cocoa')
print_and_do('{0} waf configure --edition {1} && {0} waf'.format(sys.executable, edition)) print_and_do('{0} waf configure && {0} waf'.format(sys.executable))
os.chdir('..') os.chdir('..')
app.copy_executable('cocoa/build/dupeGuru') app.copy_executable('cocoa/build/dupeGuru')
build_help(edition) build_help()
print("Copying resources and frameworks") print("Copying resources and frameworks")
image_path = ed('cocoa/{}/dupeguru.icns') image_path = 'cocoa/se/dupeguru.icns'
resources = [image_path, 'cocoa/base/dsa_pub.pem', 'build/dg_cocoa.py', 'build/help'] resources = [image_path, 'cocoa/base/dsa_pub.pem', 'build/dg_cocoa.py', 'build/help']
app.copy_resources(*resources, use_symlinks=dev) app.copy_resources(*resources, use_symlinks=dev)
app.copy_frameworks('build/Python', 'cocoalib/Sparkle.framework') app.copy_frameworks('build/Python', 'cocoalib/Sparkle.framework')
@ -171,26 +167,26 @@ def build_cocoa(edition, dev):
run_contents = tmpl.replace('{{app_path}}', app.dest) run_contents = tmpl.replace('{{app_path}}', app.dest)
open('run.py', 'wt').write(run_contents) open('run.py', 'wt').write(run_contents)
def build_qt(edition, dev, conf): def build_qt(dev):
print("Building localizations") print("Building localizations")
build_localizations('qt', edition) build_localizations('qt')
print("Building Qt stuff") print("Building Qt stuff")
print_and_do("pyrcc5 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py'))) print_and_do("pyrcc5 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
fix_qt_resource_file(op.join('qt', 'base', 'dg_rc.py')) fix_qt_resource_file(op.join('qt', 'base', 'dg_rc.py'))
build_help(edition) build_help()
print("Creating the run.py file") print("Creating the run.py file")
filereplace(op.join('qt', 'run_template.py'), 'run.py', edition=edition) shutil.copy(op.join('qt', 'run_template.py'), 'run.py')
def build_help(edition): def build_help():
print("Generating Help") print("Generating Help")
current_path = op.abspath('.') current_path = op.abspath('.')
help_basepath = op.join(current_path, 'help', 'en') help_basepath = op.join(current_path, 'help', 'en')
help_destpath = op.join(current_path, 'build', 'help'.format(edition)) help_destpath = op.join(current_path, 'build', 'help')
changelog_path = op.join(current_path, 'help', 'changelog_{}'.format(edition)) changelog_path = op.join(current_path, 'help', 'changelog_se')
tixurl = "https://github.com/hsoft/dupeguru/issues/{}" tixurl = "https://github.com/hsoft/dupeguru/issues/{}"
appname = {'se': 'dupeGuru', 'me': 'dupeGuru Music Edition', 'pe': 'dupeGuru Picture Edition'}[edition] appname = 'dupeGuru'
homepage = 'http://www.hardcoded.net/dupeguru{}/'.format('_' + edition if edition != 'se' else '') homepage = 'https://www.hardcoded.net/dupeguru/'
confrepl = {'edition': edition, 'appname': appname, 'homepage': homepage, 'language': 'en'} confrepl = {'appname': appname, 'homepage': homepage, 'language': 'en'}
changelogtmpl = op.join(current_path, 'help', 'changelog.tmpl') changelogtmpl = op.join(current_path, 'help', 'changelog.tmpl')
conftmpl = op.join(current_path, 'help', 'conf.tmpl') conftmpl = op.join(current_path, 'help', 'conf.tmpl')
sphinxgen.gen(help_basepath, help_destpath, changelog_path, tixurl, confrepl, conftmpl, changelogtmpl) sphinxgen.gen(help_basepath, help_destpath, changelog_path, tixurl, confrepl, conftmpl, changelogtmpl)
@ -199,10 +195,10 @@ def build_qt_localizations():
loc.compile_all_po(op.join('qtlib', 'locale')) loc.compile_all_po(op.join('qtlib', 'locale'))
loc.merge_locale_dir(op.join('qtlib', 'locale'), 'locale') loc.merge_locale_dir(op.join('qtlib', 'locale'), 'locale')
def build_localizations(ui, edition): def build_localizations(ui):
loc.compile_all_po('locale') loc.compile_all_po('locale')
if ui == 'cocoa': if ui == 'cocoa':
app = cocoa_app(edition) app = cocoa_app()
loc.build_cocoa_localizations(app, en_stringsfile=op.join('cocoa', 'base', 'en.lproj', 'Localizable.strings')) loc.build_cocoa_localizations(app, en_stringsfile=op.join('cocoa', 'base', 'en.lproj', 'Localizable.strings'))
locale_dest = op.join(app.resources, 'locale') locale_dest = op.join(app.resources, 'locale')
elif ui == 'qt': elif ui == 'qt':
@ -211,25 +207,13 @@ def build_localizations(ui, edition):
if op.exists(locale_dest): if op.exists(locale_dest):
shutil.rmtree(locale_dest) shutil.rmtree(locale_dest)
shutil.copytree('locale', locale_dest, ignore=shutil.ignore_patterns('*.po', '*.pot')) shutil.copytree('locale', locale_dest, ignore=shutil.ignore_patterns('*.po', '*.pot'))
if ui == 'qt' and not ISLINUX:
print("Copying qt_*.qm files into the 'locale' folder")
from PyQt5.QtCore import QLibraryInfo
trfolder = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
for lang in loc.get_langs('locale'):
qmname = 'qt_%s.qm' % lang
src = op.join(trfolder, qmname)
if op.exists(src):
copy(src, op.join('build', 'locale', qmname))
def build_updatepot(): def build_updatepot():
if ISOSX: if ISOSX:
print("Updating Cocoa strings file.") print("Updating Cocoa strings file.")
# We need to have strings from *all* editions in here, so we'll call xibless for all editions
# in dummy subfolders.
build_cocoalib_xibless('cocoalib/autogen') build_cocoalib_xibless('cocoalib/autogen')
loc.generate_cocoa_strings_from_code('cocoalib', 'cocoalib/en.lproj') loc.generate_cocoa_strings_from_code('cocoalib', 'cocoalib/en.lproj')
for edition in ('se', 'me', 'pe'): build_xibless('se', op.join('cocoa', 'autogen', 'se'))
build_xibless(edition, op.join('cocoa', 'autogen', edition))
loc.generate_cocoa_strings_from_code('cocoa', 'cocoa/base/en.lproj') loc.generate_cocoa_strings_from_code('cocoa', 'cocoa/base/en.lproj')
print("Building .pot files from source files") print("Building .pot files from source files")
print("Building core.pot") print("Building core.pot")
@ -280,7 +264,7 @@ def build_cocoa_proxy_module():
['cocoalib', 'cocoa/autogen'] ['cocoalib', 'cocoa/autogen']
) )
def build_cocoa_bridging_interfaces(edition): def build_cocoa_bridging_interfaces():
print("Building Cocoa Bridging Interfaces") print("Building Cocoa Bridging Interfaces")
import objp.o2p import objp.o2p
import objp.p2o import objp.p2o
@ -301,7 +285,7 @@ def build_cocoa_bridging_interfaces(edition):
from inter.result_table import PyResultTable, ResultTableView from inter.result_table import PyResultTable, ResultTableView
from inter.stats_label import PyStatsLabel, StatsLabelView from inter.stats_label import PyStatsLabel, StatsLabelView
from inter.app import PyDupeGuruBase, DupeGuruView from inter.app import PyDupeGuruBase, DupeGuruView
appmod = importlib.import_module('inter.app_{}'.format(edition)) appmod = importlib.import_module('inter.app_se')
allclasses = [ allclasses = [
PyGUIObject, PyColumns, PyOutline, PySelectableList, PyTable, PyBaseApp, PyGUIObject, PyColumns, PyOutline, PySelectableList, PyTable, PyBaseApp,
PyDetailsPanel, PyDirectoryOutline, PyPrioritizeDialog, PyPrioritizeList, PyProblemDialog, PyDetailsPanel, PyDirectoryOutline, PyPrioritizeDialog, PyPrioritizeList, PyProblemDialog,
@ -345,24 +329,22 @@ def build_pe_modules(ui):
move_all('_block*', 'core_pe') move_all('_block*', 'core_pe')
move_all('_cache*', 'core_pe') move_all('_cache*', 'core_pe')
def build_normal(edition, ui, dev, conf): def build_normal(ui, dev):
print("Building dupeGuru {0} with UI {1}".format(edition.upper(), ui)) print("Building dupeGuru with UI {}".format(ui))
add_to_pythonpath('.') add_to_pythonpath('.')
print("Building dupeGuru") print("Building dupeGuru")
if edition == 'pe': build_pe_modules(ui)
build_pe_modules(ui)
if ui == 'cocoa': if ui == 'cocoa':
build_cocoa(edition, dev) build_cocoa(dev)
elif ui == 'qt': elif ui == 'qt':
build_qt(edition, dev, conf) build_qt(dev)
def main(): def main():
options = parse_args() options = parse_args()
conf = json.load(open('conf.json')) ui = options.ui
edition = conf['edition'] if ui not in ('cocoa', 'qt'):
ui = conf['ui'] ui = 'cocoa' if ISOSX else 'qt'
dev = conf['dev'] if options.dev:
if dev:
print("Building in Dev mode") print("Building in Dev mode")
if options.clean: if options.clean:
for path in ['build', op.join('cocoa', 'build'), op.join('cocoa', 'autogen')]: for path in ['build', op.join('cocoa', 'build'), op.join('cocoa', 'autogen')]:
@ -371,9 +353,9 @@ def main():
if not op.exists('build'): if not op.exists('build'):
os.mkdir('build') os.mkdir('build')
if options.doc: if options.doc:
build_help(edition) build_help()
elif options.loc: elif options.loc:
build_localizations(ui, edition) build_localizations(ui)
elif options.updatepot: elif options.updatepot:
build_updatepot() build_updatepot()
elif options.mergepot: elif options.mergepot:
@ -382,17 +364,17 @@ def main():
build_normpo() build_normpo()
elif options.cocoa_ext: elif options.cocoa_ext:
build_cocoa_proxy_module() build_cocoa_proxy_module()
build_cocoa_bridging_interfaces(edition) build_cocoa_bridging_interfaces()
elif options.cocoa_compile: elif options.cocoa_compile:
os.chdir('cocoa') os.chdir('cocoa')
print_and_do('{0} waf configure --edition {1} && {0} waf'.format(sys.executable, edition)) print_and_do('{0} waf configure && {0} waf'.format(sys.executable))
os.chdir('..') os.chdir('..')
cocoa_app(edition).copy_executable('cocoa/build/dupeGuru') cocoa_app().copy_executable('cocoa/build/dupeGuru')
elif options.xibless: elif options.xibless:
build_cocoalib_xibless() build_cocoalib_xibless()
build_xibless(edition) build_xibless()
else: else:
build_normal(edition, ui, dev, conf) build_normal(ui, options.dev)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -1,45 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2009-12-30
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html
from optparse import OptionParser
import json
from hscommon.plat import ISOSX
def main(options):
if options.edition not in {'se', 'me', 'pe'}:
options.edition = 'se'
if options.ui not in {'cocoa', 'qt'}:
options.ui = 'cocoa' if ISOSX else 'qt'
build_type = 'Dev' if options.dev else 'Release'
print("Configuring dupeGuru {0} for UI {1} ({2})".format(options.edition.upper(), options.ui, build_type))
conf = {
'edition': options.edition,
'ui': options.ui,
'dev': options.dev,
}
json.dump(conf, open('conf.json', 'w'))
if __name__ == '__main__':
usage = "usage: %prog [options]"
parser = OptionParser(usage=usage)
parser.add_option(
'--edition', dest='edition',
help="dupeGuru edition to build (se, me or pe). Default is se."
)
parser.add_option(
'--ui', dest='ui',
help="Type of UI to build. 'qt' or 'cocoa'. Default is determined by your system."
)
parser.add_option(
'--dev', action='store_true', dest='dev', default=False,
help="If this flag is set, will configure for dev builds."
)
(options, args) = parser.parse_args()
main(options)

View File

@ -1,6 +1,4 @@
# Created By: Virgil Dupras # Copyright 2016 Hardcoded Software (http://www.hardcoded.net)
# Created On: 2009-12-30
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
# #
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, # This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
@ -13,13 +11,12 @@ import shutil
import json import json
from argparse import ArgumentParser from argparse import ArgumentParser
import platform import platform
import glob
from hscommon.plat import ISWINDOWS, ISLINUX from hscommon.plat import ISOSX
from hscommon.build import ( from hscommon.build import (
add_to_pythonpath, print_and_do, copy_packages, build_debian_changelog, print_and_do, copy_packages, build_debian_changelog,
copy_qt_plugins, get_module_version, filereplace, copy, setup_package_argparser, get_module_version, filereplace, copy, setup_package_argparser,
package_cocoa_app_in_dmg, copy_all, find_in_path package_cocoa_app_in_dmg, copy_all
) )
def parse_args(): def parse_args():
@ -27,90 +24,10 @@ def parse_args():
setup_package_argparser(parser) setup_package_argparser(parser)
return parser.parse_args() return parser.parse_args()
def package_cocoa(edition, args): def package_cocoa(args):
app_path = { app_path = 'build/dupeGuru.app'
'se': 'build/dupeGuru.app',
'me': 'build/dupeGuru ME.app',
'pe': 'build/dupeGuru PE.app',
}[edition]
package_cocoa_app_in_dmg(app_path, '.', args) package_cocoa_app_in_dmg(app_path, '.', args)
def package_windows(edition, dev):
if not ISWINDOWS:
print("Qt packaging only works under Windows.")
return
from cx_Freeze import setup, Executable
from PyQt5.QtCore import QLibraryInfo
add_to_pythonpath('.')
app_version = get_module_version('core_{}'.format(edition))
distdir = 'dist'
if op.exists(distdir):
shutil.rmtree(distdir)
if not dev:
# Copy qt plugins
plugin_dest = distdir
plugin_names = ['accessible', 'codecs', 'iconengines', 'imageformats']
copy_qt_plugins(plugin_names, plugin_dest)
# Since v4.2.3, cx_freeze started to falsely include tkinter in the package. We exclude it
# explicitly because of that.
options = {
'build_exe': {
'includes': 'atexit',
'excludes': ['tkinter'],
'bin_excludes': ['icudt51', 'icuin51.dll', 'icuuc51.dll'],
'icon': 'images\\dg{0}_logo.ico'.format(edition),
'include_msvcr': True,
},
'install_exe': {
'install_dir': 'dist',
}
}
executables = [
Executable(
'run.py',
base='Win32GUI',
targetDir=distdir,
targetName={'se': 'dupeGuru', 'me': 'dupeGuru ME', 'pe': 'dupeGuru PE'}[edition] + '.exe',
)
]
setup(
script_args=['install'],
options=options,
executables=executables
)
print("Removing useless files")
# Debug info that cx_freeze brings in.
for fn in glob.glob(op.join(distdir, '*', '*.pdb')):
os.remove(fn)
print("Copying forgotten DLLs")
qtlibpath = QLibraryInfo.location(QLibraryInfo.LibrariesPath)
shutil.copy(op.join(qtlibpath, 'libEGL.dll'), distdir)
shutil.copy(find_in_path('msvcp110.dll'), distdir)
print("Copying the rest")
help_path = op.join('build', 'help')
print("Copying {} to dist\\help".format(help_path))
shutil.copytree(help_path, op.join(distdir, 'help'))
locale_path = op.join('build', 'locale')
print("Copying {} to dist\\locale".format(locale_path))
shutil.copytree(locale_path, op.join(distdir, 'locale'))
# AdvancedInstaller.com has to be in your PATH
# this is so we don'a have to re-commit installer.aip at every version change
installer_file = 'installer.aip'
installer_path = op.join('qt', edition, installer_file)
shutil.copy(installer_path, 'installer_tmp.aip')
print_and_do('AdvancedInstaller.com /edit installer_tmp.aip /SetVersion %s' % app_version)
print_and_do('AdvancedInstaller.com /build installer_tmp.aip -force')
os.remove('installer_tmp.aip')
if op.exists('installer_tmp.back.aip'):
os.remove('installer_tmp.back.aip')
def copy_files_to_package(destpath, packages, with_so): def copy_files_to_package(destpath, packages, with_so):
# when with_so is true, we keep .so files in the package, and otherwise, we don't. We need this # when with_so is true, we keep .so files in the package, and otherwise, we don't. We need this
# flag because when building debian src pkg, we *don't* want .so files (they're compiled later) # flag because when building debian src pkg, we *don't* want .so files (they're compiled later)
@ -126,71 +43,70 @@ def copy_files_to_package(destpath, packages, with_so):
shutil.copytree(op.join('build', 'locale'), op.join(destpath, 'locale')) shutil.copytree(op.join('build', 'locale'), op.join(destpath, 'locale'))
compileall.compile_dir(destpath) compileall.compile_dir(destpath)
def package_debian_distribution(edition, distribution): def package_debian_distribution(distribution):
app_version = get_module_version('core_{}'.format(edition)) app_version = get_module_version('core_se')
version = '{}~{}'.format(app_version, distribution) version = '{}~{}'.format(app_version, distribution)
ed = lambda s: s.format(edition) destpath = op.join('build', 'dupeguru-{}'.format(version))
destpath = op.join('build', 'dupeguru-{0}-{1}'.format(edition, version))
srcpath = op.join(destpath, 'src') srcpath = op.join(destpath, 'src')
packages = ['hscommon', 'core', ed('core_{0}'), 'qtlib', 'qt', 'send2trash'] packages = [
if edition == 'me': 'hscommon', 'core', 'core_se', 'core_me', 'core_pe', 'qtlib', 'qt', 'send2trash',
packages.append('hsaudiotag') 'hsaudiotag'
]
copy_files_to_package(srcpath, packages, with_so=False) copy_files_to_package(srcpath, packages, with_so=False)
if edition == 'pe': os.mkdir(op.join(destpath, 'modules'))
os.mkdir(op.join(destpath, 'modules')) copy_all(op.join('core_pe', 'modules', '*.*'), op.join(destpath, 'modules'))
copy_all(op.join('core_pe', 'modules', '*.*'), op.join(destpath, 'modules')) copy(op.join('qt', 'pe', 'modules', 'block.c'), op.join(destpath, 'modules', 'block_qt.c'))
copy(op.join('qt', 'pe', 'modules', 'block.c'), op.join(destpath, 'modules', 'block_qt.c')) copy(op.join('pkg', 'debian', 'build_pe_modules.py'), op.join(destpath, 'build_pe_modules.py'))
copy(op.join('pkg', 'debian', 'build_pe_modules.py'), op.join(destpath, 'build_pe_modules.py'))
debdest = op.join(destpath, 'debian') debdest = op.join(destpath, 'debian')
debskel = op.join('pkg', 'debian') debskel = op.join('pkg', 'debian')
os.makedirs(debdest) os.makedirs(debdest)
debopts = json.load(open(op.join(debskel, ed('{}.json')))) debopts = json.load(open(op.join(debskel, 'se.json')))
for fn in ['compat', 'copyright', 'dirs', 'rules']: for fn in ['compat', 'copyright', 'dirs', 'rules']:
copy(op.join(debskel, fn), op.join(debdest, fn)) copy(op.join(debskel, fn), op.join(debdest, fn))
filereplace(op.join(debskel, 'control'), op.join(debdest, 'control'), **debopts) filereplace(op.join(debskel, 'control'), op.join(debdest, 'control'), **debopts)
filereplace(op.join(debskel, 'Makefile'), op.join(destpath, 'Makefile'), **debopts) filereplace(op.join(debskel, 'Makefile'), op.join(destpath, 'Makefile'), **debopts)
filereplace(op.join(debskel, 'dupeguru.desktop'), op.join(debdest, ed('dupeguru_{}.desktop')), **debopts) filereplace(op.join(debskel, 'dupeguru.desktop'), op.join(debdest, 'dupeguru.desktop'), **debopts)
changelogpath = op.join('help', ed('changelog_{}')) changelogpath = op.join('help', 'changelog_se')
changelog_dest = op.join(debdest, 'changelog') changelog_dest = op.join(debdest, 'changelog')
project_name = debopts['pkgname'] project_name = debopts['pkgname']
from_version = {'se': '2.9.2', 'me': '5.7.2', 'pe': '1.8.5'}[edition] from_version = '2.9.2'
build_debian_changelog( build_debian_changelog(
changelogpath, changelog_dest, project_name, from_version=from_version, changelogpath, changelog_dest, project_name, from_version=from_version,
distribution=distribution distribution=distribution
) )
shutil.copy(op.join('images', ed('dg{0}_logo_128.png')), srcpath) shutil.copy(op.join('images', 'dgse_logo_128.png'), srcpath)
os.chdir(destpath) os.chdir(destpath)
cmd = "dpkg-buildpackage -S" cmd = "dpkg-buildpackage -S"
os.system(cmd) os.system(cmd)
os.chdir('../..') os.chdir('../..')
def package_debian(edition): def package_debian():
print("Packaging for Ubuntu") print("Packaging for Ubuntu")
for distribution in ['trusty', 'utopic']: for distribution in ['trusty', 'utopic']:
package_debian_distribution(edition, distribution) package_debian_distribution(distribution)
def package_arch(edition): def package_arch():
# For now, package_arch() will only copy the source files into build/. It copies less packages # For now, package_arch() will only copy the source files into build/. It copies less packages
# than package_debian because there are more python packages available in Arch (so we don't # than package_debian because there are more python packages available in Arch (so we don't
# need to include them). # need to include them).
print("Packaging for Arch") print("Packaging for Arch")
ed = lambda s: s.format(edition) srcpath = op.join('build', 'dupeguru-arch')
srcpath = op.join('build', ed('dupeguru-{}-arch')) packages = [
packages = ['hscommon', 'core', ed('core_{0}'), 'qtlib', 'qt', 'send2trash'] 'hscommon', 'core', 'core_se', 'core_me', 'core_pe', 'qtlib', 'qt', 'send2trash',
if edition == 'me': 'hsaudiotag',
packages.append('hsaudiotag') ]
copy_files_to_package(srcpath, packages, with_so=True) copy_files_to_package(srcpath, packages, with_so=True)
shutil.copy(op.join('images', ed('dg{}_logo_128.png')), srcpath) shutil.copy(op.join('images', 'dgse_logo_128.png'), srcpath)
debopts = json.load(open(op.join('pkg', 'arch', ed('{}.json')))) debopts = json.load(open(op.join('pkg', 'arch', 'se.json')))
filereplace(op.join('pkg', 'arch', 'dupeguru.desktop'), op.join(srcpath, ed('dupeguru-{}.desktop')), **debopts) filereplace(op.join('pkg', 'arch', 'dupeguru.desktop'), op.join(srcpath, 'dupeguru.desktop'), **debopts)
def package_source_tgz(edition): def package_source_tgz():
if not op.exists('deps'): if not op.exists('deps'):
print("Downloading PyPI dependencies") print("Downloading PyPI dependencies")
print_and_do('./download_deps.sh') print_and_do('./download_deps.sh')
print("Creating git archive") print("Creating git archive")
app_version = get_module_version('core_{}'.format(edition)) app_version = get_module_version('core_se')
name = 'dupeguru-{}-src-{}.tar'.format(edition, app_version) name = 'dupeguru-src-{}.tar'.format(app_version)
dest = op.join('build', name) dest = op.join('build', name)
print_and_do('git archive -o {} HEAD'.format(dest)) print_and_do('git archive -o {} HEAD'.format(dest))
print("Adding dependencies and wrapping up") print("Adding dependencies and wrapping up")
@ -199,31 +115,23 @@ def package_source_tgz(edition):
def main(): def main():
args = parse_args() args = parse_args()
conf = json.load(open('conf.json')) ui = 'cocoa' if ISOSX else 'qt'
edition = conf['edition']
ui = conf['ui']
dev = conf['dev']
if args.src_pkg: if args.src_pkg:
print("Creating source package for dupeGuru {}".format(edition.upper())) print("Creating source package for dupeGuru")
package_source_tgz(edition) package_source_tgz()
return return
print("Packaging dupeGuru {0} with UI {1}".format(edition.upper(), ui)) print("Packaging dupeGuru with UI {}".format(ui))
if ui == 'cocoa': if ui == 'cocoa':
package_cocoa(edition, args) package_cocoa(args)
elif ui == 'qt': elif ui == 'qt':
if ISWINDOWS: if not args.arch_pkg:
package_windows(edition, dev) distname, _, _ = platform.dist()
elif ISLINUX:
if not args.arch_pkg:
distname, _, _ = platform.dist()
else:
distname = 'arch'
if distname == 'arch':
package_arch(edition)
else:
package_debian(edition)
else: else:
print("Qt packaging only works under Windows or Linux.") distname = 'arch'
if distname == 'arch':
package_arch()
else:
package_debian()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -1,8 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net) # Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
# #
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, # This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
import sys import sys
@ -13,16 +13,12 @@ from PyQt5.QtCore import QCoreApplication, QSettings
from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from hscommon.plat import ISWINDOWS
from hscommon.trans import install_gettext_trans_under_qt from hscommon.trans import install_gettext_trans_under_qt
from qtlib.error_report_dialog import install_excepthook from qtlib.error_report_dialog import install_excepthook
from qtlib.util import setupQtLogging from qtlib.util import setupQtLogging
from qt.base import dg_rc from qt.base import dg_rc
from qt.base.platform import BASE_PATH from qt.base.platform import BASE_PATH
from core_{edition} import __version__, __appname__ from core_se import __version__, __appname__
if ISWINDOWS:
import qt.base.cxfreeze_fix
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)
@ -36,7 +32,7 @@ def main():
install_gettext_trans_under_qt(locale_folder, lang) install_gettext_trans_under_qt(locale_folder, lang)
# Many strings are translated at import time, so this is why we only import after the translator # Many strings are translated at import time, so this is why we only import after the translator
# has been installed # has been installed
from qt.{edition}.app import DupeGuru from qt.se.app import DupeGuru
app.setWindowIcon(QIcon(QPixmap(":/{0}".format(DupeGuru.LOGO_NAME)))) app.setWindowIcon(QIcon(QPixmap(":/{0}".format(DupeGuru.LOGO_NAME))))
dgapp = DupeGuru() dgapp = DupeGuru()
install_excepthook('https://github.com/hsoft/dupeguru/issues') install_excepthook('https://github.com/hsoft/dupeguru/issues')