diff --git a/.gitignore b/.gitignore
index 05a98150..69bd4df2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,6 @@
# Vagrant
.vagrant
+
+# Package files
+*.pkg
diff --git a/BUILD.md b/BUILD.md
index 58020756..1cf51fec 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -59,3 +59,11 @@ You will need Microsoft Visual Studio 2013 to compile freelan. All projects come
The root directory also contains a solution file (`.sln`) that references all the sub-projects.
The resulting binaries will be located in the [install](install) directory.
+
+### Mac OSX
+
+On Mac OSX, an additional SCons target exists to build the freelan installation package:
+
+> scons package
+
+The package will be generated at the root of the repository.
diff --git a/SConscript b/SConscript
index 75468b42..995362ec 100644
--- a/SConscript
+++ b/SConscript
@@ -57,24 +57,25 @@ for x in Glob('apps/*'):
samples = []
-for x in Glob('samples/*'):
- libname = os.path.basename(str(x))
+if env.mode != 'retail':
+ for x in Glob('samples/*'):
+ libname = os.path.basename(str(x))
- if not sys.platform.startswith('linux'):
- if libname in 'netlinkplus':
- continue
+ if not sys.platform.startswith('linux'):
+ if libname in 'netlinkplus':
+ continue
- for y in x.glob('*'):
- sconscript_path = y.File('SConscript')
+ for y in x.glob('*'):
+ sconscript_path = y.File('SConscript')
- if sconscript_path.exists():
- name = 'sample_%s_%s' % (libname, os.path.basename(str(y)))
- sample = SConscript(sconscript_path, exports='env dirs name')
- samples.extend(sample)
+ if sconscript_path.exists():
+ name = 'sample_%s_%s' % (libname, os.path.basename(str(y)))
+ sample = SConscript(sconscript_path, exports='env dirs name')
+ samples.extend(sample)
- if env.debug:
- samples.extend(env.SymLink(y.File('%sd' % os.path.basename(str(y))).srcnode(), sample))
- else:
- samples.extend(env.SymLink(y.File(os.path.basename(str(y))).srcnode(), sample))
+ if env.mode == 'release':
+ samples.extend(env.SymLink(y.File('%sd' % os.path.basename(str(y))).srcnode(), sample))
+ else:
+ samples.extend(env.SymLink(y.File(os.path.basename(str(y))).srcnode(), sample))
Return('libraries includes apps samples configurations')
diff --git a/SConstruct b/SConstruct
index a86c455d..3675287a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -40,12 +40,11 @@ class FreelanEnvironment(Environment):
A freelan specific environment class.
"""
- def __init__(self, debug, prefix, **kwargs):
+ def __init__(self, mode, prefix, **kwargs):
"""
Initialize the environment.
- :param debug: A boolean value that indicates whether to set debug flags
- in the environment.
+ :param mode: The compilation mode.
:param prefix: The installation prefix.
"""
super(FreelanEnvironment, self).__init__(**kwargs)
@@ -71,7 +70,7 @@ class FreelanEnvironment(Environment):
if flag in os.environ:
self[flag] = Split(os.environ[flag])
- self.debug = debug
+ self.mode = mode
self.prefix = prefix
if os.path.basename(self['CXX']) == 'clang++':
@@ -98,7 +97,7 @@ class FreelanEnvironment(Environment):
self.Append(CXXFLAGS=['--stdlib=libc++'])
self.Append(LDFLAGS=['--stdlib=libc++'])
- if self.debug:
+ if self.mode == 'debug':
self.Append(CXXFLAGS=['-g'])
self.Append(CXXFLAGS='-DFREELAN_DEBUG=1')
else:
@@ -151,20 +150,29 @@ mode = GetOption('mode')
prefix = os.path.abspath(GetOption('prefix'))
if mode in ('all', 'release'):
- env = FreelanEnvironment(debug=False, prefix=prefix)
- libraries, includes, apps, samples, configurations = SConscript('SConscript', exports='env', variant_dir=os.path.join('build', 'release'))
+ env = FreelanEnvironment(mode='release', prefix=prefix)
+ libraries, includes, apps, samples, configurations = SConscript('SConscript', exports='env', variant_dir=os.path.join('build', env.mode))
install = env.Install(os.path.join(prefix, 'bin'), apps)
install.extend(env.Install(os.path.join(prefix, 'etc', 'freelan'), configurations))
+
Alias('install', install)
Alias('apps', apps)
Alias('samples', samples)
Alias('all', install + apps + samples)
if mode in ('all', 'debug'):
- env = FreelanEnvironment(debug=True, prefix=prefix)
- libraries, includes, apps, samples, configurations = SConscript('SConscript', exports='env', variant_dir=os.path.join('build', 'debug'))
+ env = FreelanEnvironment(mode='debug', prefix=prefix)
+ libraries, includes, apps, samples, configurations = SConscript('SConscript', exports='env', variant_dir=os.path.join('build', env.mode))
Alias('apps', apps)
Alias('samples', samples)
Alias('all', apps + samples)
+if sys.platform.startswith('darwin'):
+ retail_prefix = '/usr/local'
+ env = FreelanEnvironment(mode='retail', prefix=retail_prefix)
+ libraries, includes, apps, samples, configurations = SConscript('SConscript', exports='env', variant_dir=os.path.join('build', env.mode))
+ package = SConscript('packaging/osx/SConscript', exports='env apps configurations retail_prefix')
+ install_package = env.Install('.', package)
+ Alias('package', install_package)
+
Default('install')
diff --git a/defines.py b/defines.py
index 03859bda..0215df6e 100644
--- a/defines.py
+++ b/defines.py
@@ -71,6 +71,10 @@ def version(self):
return self._version
+ @property
+ def version_str(self):
+ return '%s.%s' % (self.version.major, self.version.minor)
+
@property
def date(self):
if self._date is None:
@@ -125,6 +129,7 @@ def register_into(self, env):
action=self.action,
emitter=self.emitter,
)})
+ env.defines = self
def generate_defines(self, target):
"""
diff --git a/packaging/osx/.gitignore b/packaging/osx/.gitignore
index faa418a1..b6967424 100644
--- a/packaging/osx/.gitignore
+++ b/packaging/osx/.gitignore
@@ -1,2 +1,3 @@
-*.pkg
-org.freelan.freelan
+distribution.xml
+root/
+resources/conclusion.html
diff --git a/packaging/osx/Makefile b/packaging/osx/Makefile
deleted file mode 100644
index f1d99a24..00000000
--- a/packaging/osx/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-PRODUCT_NAME=freelan
-IDENTIFIER=org.freelan.freelan
-VERSION=2.0
-DAEMON_PACKAGE=${IDENTIFIER}.pkg
-FINAL_PACKAGE=${PRODUCT_NAME}_${VERSION}.pkg
-
-default: package
-
-clean:
- rm -rf ${IDENTIFIER}
- rm -f ${DAEMON_PACKAGE}
- rm -f ${FINAL_PACKAGE}
-
-package: clean
- mkdir -p ${IDENTIFIER}
- mkdir -p ${IDENTIFIER}/usr/local
- mkdir -p ${IDENTIFIER}/usr/local/share/freelan
- mkdir -p ${IDENTIFIER}/Library/LaunchDaemons
- cp -r ../../install/* ${IDENTIFIER}/usr/local/
- cp org.freelan.freelan.plist ${IDENTIFIER}/Library/LaunchDaemons/
- cp uninstall.sh ${IDENTIFIER}/usr/local/share/freelan/
- pkgbuild --root ${IDENTIFIER} --identifier ${IDENTIFIER} --version ${VERSION} --ownership recommended --scripts scripts ${DAEMON_PACKAGE}
- productbuild --distribution distribution.xml --resources resources --package-path . --version ${VERSION} ${FINAL_PACKAGE}
diff --git a/packaging/osx/README.md b/packaging/osx/README.md
index 665c5193..7797897c 100644
--- a/packaging/osx/README.md
+++ b/packaging/osx/README.md
@@ -1,3 +1,5 @@
# Mac OS X Installer
-To build the Mac OS X package, just type "make".
+To build the Mac OS X package, just type "scons -u .".
+
+You can also build it from the repository root by typing "scons package".
diff --git a/packaging/osx/SConscript b/packaging/osx/SConscript
new file mode 100644
index 00000000..f2b712a5
--- /dev/null
+++ b/packaging/osx/SConscript
@@ -0,0 +1,104 @@
+import os
+import pkgbuild
+import productbuild
+import plist
+import generate_script
+import template
+
+
+def relative(path):
+ return path.lstrip('/')
+
+
+Import('env apps configurations retail_prefix')
+
+env = env.Clone()
+
+for module in [pkgbuild, productbuild, plist, generate_script, template]:
+ module.generate(env)
+
+root = env.Dir('root')
+scripts = env.Dir('scripts')
+options = {
+ 'identifier': 'org.freelan.freelan',
+ 'version': env.defines.version_str,
+ 'ownership': 'recommended',
+}
+resources = env.Dir('resources')
+distribution_template = env.File('distribution.xml.in')
+conclusion_template = resources.File('conclusion.html.in')
+
+bin_path = os.path.join(retail_prefix, 'bin')
+etc_freelan_path = os.path.join(retail_prefix, 'etc/freelan')
+share_freelan_path = os.path.join(retail_prefix, 'share/freelan')
+uninstall_script = os.path.join(share_freelan_path, 'uninstall.sh')
+launch_daemon_script = os.path.join(
+ '/Library/LaunchDaemons',
+ options['identifier'] + '.plist',
+)
+
+uninstall_script_source = env.Value([
+ '/bin/launchctl unload ' + launch_daemon_script,
+ 'rm -f ' + launch_daemon_script,
+ 'rm -f ' + os.path.join(bin_path, apps[0].name),
+ 'rm -rf ' + etc_freelan_path,
+])
+launch_daemon_script_source = env.Value({
+ 'Label': options['identifier'],
+ 'ProgramArguments': [
+ os.path.join(bin_path, apps[0].name),
+ '-c',
+ os.path.join(etc_freelan_path, configurations[0].name),
+ '-f',
+ ],
+ 'RunAtLoad': True,
+ 'KeepAlive': True,
+})
+
+env.Install(
+ root.Dir(relative(bin_path)),
+ apps,
+)
+env.Install(
+ root.Dir(relative(etc_freelan_path)),
+ configurations,
+)
+env.GenerateScript(
+ root.File(relative(uninstall_script)),
+ uninstall_script_source,
+)
+env.Plist(
+ root.File(relative(launch_daemon_script)),
+ launch_daemon_script_source,
+)
+
+package = env.PkgBuild(
+ target=options['identifier'] + '.pkg',
+ source=root,
+ PKGBUILD_OPTIONS=env.Value(options),
+ PKGBUILD_SCRIPTS=scripts,
+)
+distribution_file = env.Template(
+ source=distribution_template,
+ TEMPLATE_DICT=env.Value({'version': env.defines.version_str}),
+)
+conclusion_file = env.Template(
+ source=conclusion_template,
+ TEMPLATE_DICT=env.Value({
+ 'configuration_file': os.path.join(
+ etc_freelan_path,
+ configurations[0].name,
+ )
+ }),
+)
+final_package = env.ProductBuild(
+ target='freelan_{version}.pkg'.format(version=env.defines.version_str),
+ source=distribution_file,
+ PRODUCTBUILD_OPTIONS=env.Value({
+ 'version': env.defines.version_str,
+ }),
+ PRODUCTBUILD_RESOURCES=resources,
+ PRODUCTBUILD_PACKAGE_PATH=[env.Dir('.')],
+)
+
+Return('final_package')
diff --git a/packaging/osx/distribution.xml b/packaging/osx/distribution.xml.in
similarity index 84%
rename from packaging/osx/distribution.xml
rename to packaging/osx/distribution.xml.in
index fbc67bd8..4a1a289c 100644
--- a/packaging/osx/distribution.xml
+++ b/packaging/osx/distribution.xml.in
@@ -1,6 +1,6 @@
You may go to www.freelan.org for configuration instructions.
- - diff --git a/packaging/osx/resources/conclusion.html.in b/packaging/osx/resources/conclusion.html.in new file mode 100644 index 00000000..8a998011 --- /dev/null +++ b/packaging/osx/resources/conclusion.html.in @@ -0,0 +1,9 @@ + + + + +Go and edit the configuration file at {configuration_file}
to get started !
You may also check the official website for detailed configuration instructions.
+ + diff --git a/packaging/osx/template.py b/packaging/osx/template.py new file mode 100644 index 00000000..563b0fd6 --- /dev/null +++ b/packaging/osx/template.py @@ -0,0 +1,32 @@ +"""A SCons builder for template files""" + + +def template_emitter(target, source, env): + env.Depends(target, env['TEMPLATE_DICT']) + + return (target, source) + + +def template_action(target, source, env): + _dict = env['TEMPLATE_DICT'].value + + template = source[0].get_contents() + + with open(target[0].abspath, 'w') as targf: + targf.write( + template.format(**_dict) + ) + + +def generate(env): + env.Append(TEMPLATE_DICT=env.Value({})) + + import SCons.Builder + + template_builder = SCons.Builder.Builder( + action=template_action, + emitter=template_emitter, + src_suffix='.in', + ) + + env.Append(BUILDERS={'Template': template_builder}) diff --git a/packaging/osx/uninstall.sh b/packaging/osx/uninstall.sh deleted file mode 100755 index 9d76a85e..00000000 --- a/packaging/osx/uninstall.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -/bin/launchctl unload /Library/LaunchDaemons/org.freelan.freelan.plist -rm -f /Library/LaunchDaemons/org.freelan.freelan.plist -rm -f /usr/local/sbin/freelan -rm -f /usr/local/etc/freelan/freelan.cfg