Add packaging script and (possibly) support for OS X 10.9.

This commit is contained in:
Kyle Neideck 2017-06-11 19:19:31 +10:00
parent 8d1adf25bb
commit 5e4556b49d
No known key found for this signature in database
GPG key ID: CAA8D9B8E39EC18C
17 changed files with 656 additions and 187 deletions

View file

@ -62,6 +62,15 @@ script:
- if ls -la "/usr/local/libexec/BGMXPCHelper.xpc"; then false; fi
- if ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"; then false; fi
- if ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"; then false; fi
# Build the .pkg installer.
- ./package.sh
# Install the .pkg.
- sudo installer -pkg Background-Music-*/BackgroundMusic-*.pkg -target / -verbose -dumplog
# Check the BGM dirs and files were installed again.
- ls -la "/Applications/Background Music.app"
- ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
- ls -la "/usr/local/libexec/BGMXPCHelper.xpc" || ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"
- ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
# Post on IRC when Travis builds finish.
notifications:
irc: "irc.freenode.org#backgroundmusic"

View file

@ -36,6 +36,9 @@
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C4699461BD5C0E400F78043 /* BGMiTunes.m */; };
1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */; };
1C50FF631EC9F4490031A6EA /* BGMAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CF5423A1EAAEE4300445AD8 /* BGMAudioDevice.cpp */; };
1C533C7A1EED28B700270802 /* uninstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 1C533C791EED28B700270802 /* uninstall.sh */; };
1C533C7B1EED2F6200270802 /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
1C533C7C1EED2F8A00270802 /* com.bearisdriving.BGM.XPCHelper.plist.template in Resources */ = {isa = PBXBuildFile; fileRef = 2769728D1CAFCEFD007A2F7C /* com.bearisdriving.BGM.XPCHelper.plist.template */; };
1CB8B33D1BBA75EF000E2DD1 /* BGMAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* BGMAppDelegate.mm */; };
1CB8B33F1BBA75EF000E2DD1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33E1BBA75EF000E2DD1 /* main.m */; };
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */; };
@ -127,8 +130,6 @@
2743CA221D86DE960089613B /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C1963021BCAC160008A4DF7 /* CoreAudio.framework */; };
2743CA231D86DEA70089613B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
274827951E11052500B31D8D /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1CB8B3421BBA75EF000E2DD1 /* MainMenu.xib */; };
2769728F1CAFF22C007A2F7C /* com.bearisdriving.BGM.XPCHelper.plist.template in Resources */ = {isa = PBXBuildFile; fileRef = 2769728D1CAFCEFD007A2F7C /* com.bearisdriving.BGM.XPCHelper.plist.template */; };
276972921CB1603E007A2F7C /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
277170161CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 277170151CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m */; };
2795973B1C982E4E00A002FB /* BGMXPCListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2795973A1C982E4E00A002FB /* BGMXPCListener.mm */; };
279F48771DD6D73A00768A85 /* BGMHermes.m in Sources */ = {isa = PBXBuildFile; fileRef = 279F48761DD6D73900768A85 /* BGMHermes.m */; };
@ -214,6 +215,7 @@
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; };
1C46994D1BD7694C00F78043 /* BGMDeviceControlSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlSync.h; sourceTree = "<group>"; };
1C50FF641EC9F4500031A6EA /* libPublicUtility.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libPublicUtility.a; path = "../../../Library/Developer/Xcode/DerivedData/BGM-cgeucfvbrkmtbnhewbqjwrqspirp/Build/Products/Debug/libPublicUtility.a"; sourceTree = "<group>"; };
1C533C791EED28B700270802 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = uninstall.sh; path = ../../uninstall.sh; sourceTree = "<group>"; };
1C8034C21BDAFD5700668E00 /* CAPThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAPThread.cpp; path = PublicUtility/CAPThread.cpp; sourceTree = "<group>"; };
1C8034C31BDAFD5700668E00 /* CAPThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAPThread.h; path = PublicUtility/CAPThread.h; sourceTree = "<group>"; };
1CB8B3361BBA75EF000E2DD1 /* Background Music.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Background Music.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -518,6 +520,7 @@
1CED61681C3081C2002CAFCF /* LICENSE */,
1CC1DF951BE8607700FB8FE4 /* Images.xcassets */,
1CB8B33A1BBA75EF000E2DD1 /* Info.plist */,
1C533C791EED28B700270802 /* uninstall.sh */,
1CB8B33E1BBA75EF000E2DD1 /* main.m */,
);
name = "Supporting Files";
@ -795,6 +798,7 @@
buildActionMask = 2147483647;
files = (
274827951E11052500B31D8D /* MainMenu.xib in Resources */,
1C533C7A1EED28B700270802 /* uninstall.sh in Resources */,
1CED61691C3081C2002CAFCF /* LICENSE in Resources */,
1C2FC3041EB4D6E700A76592 /* BGMApp.sdef in Resources */,
1CC1DF961BE8607700FB8FE4 /* Images.xcassets in Resources */,
@ -812,8 +816,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
276972921CB1603E007A2F7C /* safe_install_dir.sh in Resources */,
2769728F1CAFF22C007A2F7C /* com.bearisdriving.BGM.XPCHelper.plist.template in Resources */,
1C533C7C1EED2F8A00270802 /* com.bearisdriving.BGM.XPCHelper.plist.template in Resources */,
1C533C7B1EED2F6200270802 /* safe_install_dir.sh in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1114,7 +1118,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
@ -1210,7 +1214,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
@ -1284,7 +1288,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = NO;
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;

View file

@ -64,22 +64,58 @@ static float const kStatusBarIconPadding = 0.25;
}
haveShownXPCHelperErrorMessage = NO;
// Set up the status bar item
[self initStatusBarItem];
}
// Set up the status bar item. (The thing you click to show BGMApp's UI.)
- (void) initStatusBarItem {
statusBarItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
// Set the icon
NSImage* icon = [NSImage imageNamed:@"FermataIcon"];
// NSStatusItem doesn't have the "button" property on OS X 10.9.
BOOL buttonAvailable = (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10);
if (icon != nil) {
CGFloat lengthMinusPadding = [[statusBarItem button] frame].size.height * (1 - kStatusBarIconPadding);
NSRect statusBarItemFrame;
if (buttonAvailable) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
statusBarItemFrame = statusBarItem.button.frame;
#pragma clang diagnostic pop
} else {
// OS X 10.9 fallback. I haven't tested this (or anything else on 10.9).
statusBarItemFrame = statusBarItem.view.frame;
}
CGFloat lengthMinusPadding = statusBarItemFrame.size.height * (1 - kStatusBarIconPadding);
[icon setSize:NSMakeSize(lengthMinusPadding, lengthMinusPadding)];
// Make the icon a "template image" so it gets drawn colour-inverted when it's highlighted or the status
// bar's in dark mode
[icon setTemplate:YES];
statusBarItem.button.image = icon;
if (buttonAvailable) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
statusBarItem.button.image = icon;
#pragma clang diagnostic pop
} else {
statusBarItem.image = icon;
}
} else {
// If our icon is missing for some reason, fallback to a fermata character (1D110)
statusBarItem.button.title = @"𝄐";
if (buttonAvailable) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
statusBarItem.button.title = @"𝄐";
#pragma clang diagnostic pop
} else {
statusBarItem.title = @"𝄐";
}
}
// Set the main menu

View file

@ -124,9 +124,12 @@ static CGFloat const kAppVolumeViewInitialHeight = 20;
// NSMenuItem didn't implement NSAccessibility before OS X SDK 10.12.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 // MAC_OS_X_VERSION_10_12
if ([self respondsToSelector:@selector(accessibilityTitle)]) {
if ([appVolItem respondsToSelector:@selector(setAccessibilityTitle:)]) {
// TODO: This doesn't show up in Accessibility Inspector for me. Not sure why.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
appVolItem.accessibilityTitle = [NSString stringWithFormat:@"%@", [app localizedName]];
#pragma clang diagnostic pop
}
#endif
@ -365,7 +368,12 @@ static CGFloat const kAppVolumeViewInitialHeight = 20;
// when they have non-default values.
[ctx showHideExtraControls:self];
self.accessibilityTitle = @"More options";
if ([self respondsToSelector:@selector(setAccessibilityTitle:)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
self.accessibilityTitle = @"More options";
#pragma clang diagnostic pop
}
}
@end
@ -391,7 +399,12 @@ static CGFloat const kAppVolumeViewInitialHeight = 20;
self.maxValue = kAppRelativeVolumeMaxRawValue;
self.minValue = kAppRelativeVolumeMinRawValue;
self.accessibilityTitle = [NSString stringWithFormat:@"Volume for %@", [app localizedName]];
if ([self respondsToSelector:@selector(setAccessibilityTitle:)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
self.accessibilityTitle = [NSString stringWithFormat:@"Volume for %@", [app localizedName]];
#pragma clang diagnostic pop
}
}
// We have to handle snapping for volume sliders ourselves because adding a tick mark (snap point) in Interface Builder
@ -442,7 +455,12 @@ static CGFloat const kAppVolumeViewInitialHeight = 20;
self.minValue = kAppPanLeftRawValue;
self.maxValue = kAppPanRightRawValue;
self.accessibilityTitle = [NSString stringWithFormat:@"Pan for %@", [app localizedName]];
if ([self respondsToSelector:@selector(setAccessibilityTitle:)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
self.accessibilityTitle = [NSString stringWithFormat:@"Pan for %@", [app localizedName]];
#pragma clang diagnostic pop
}
}
- (void) setPanPosition:(NSNumber *)panPosition {

View file

@ -17,7 +17,7 @@
// BGMAutoPauseMusic.m
// BGMApp
//
// Copyright © 2016 Kyle Neideck
// Copyright © 2016, 2017 Kyle Neideck
//
// Self Include
@ -84,7 +84,18 @@ static Float32 const kUnpauseDelayWeightingFactor = 0.25f;
enabled = NO;
wePaused = NO;
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
dispatch_queue_attr_t attr;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
if (&dispatch_queue_attr_make_with_qos_class) {
attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
} else {
// OS X 10.9 fallback
attr = DISPATCH_QUEUE_SERIAL;
}
#pragma clang diagnostic pop
listenerQueue = dispatch_queue_create("com.bearisdriving.BGM.AutoPauseMusic.Listener", attr);
pauseUnpauseMusicQueue = dispatch_queue_create("com.bearisdriving.BGM.AutoPauseMusic.PauseUnpauseMusic", attr);

View file

@ -50,12 +50,25 @@ BGMDeviceControlsList::BGMDeviceControlsList(AudioObjectID inBGMDevice,
BGMAssert((mBGMDevice.GetObjectID() == mAudioSystem.GetAudioDeviceForUID(CFSTR(kBGMDeviceUID)) ||
mBGMDevice.GetObjectID() == kAudioObjectUnknown),
"BGMDeviceControlsList::BGMDeviceControlsList: Given device is not BGMDevice");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
mCanToggleDeviceOnSystem = (&dispatch_block_wait &&
&dispatch_block_cancel &&
&dispatch_block_testcancel &&
&dispatch_queue_attr_make_with_qos_class);
#pragma clang diagnostic pop
}
BGMDeviceControlsList::~BGMDeviceControlsList()
{
CAMutex::Locker locker(mMutex);
if(!mDeviceTogglingInitialised)
{
return;
}
BGMLogAndSwallowExceptions("BGMDeviceControlsList::~BGMDeviceControlsList", [&] {
mAudioSystem.RemovePropertyListenerBlock(CAPropertyAddress(kAudioHardwarePropertyDevices),
mListenerQueue,
@ -75,7 +88,10 @@ BGMDeviceControlsList::~BGMDeviceControlsList()
// Note that if mDisableNullDeviceBlock is currently running this will return after it
// finishes and if it's already run this will return immediately. So we don't have to
// worry about ending up waiting for mDisableNullDeviceBlock when it isn't queued.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
bool timedOut = dispatch_block_wait(disableNullDeviceBlock, kDisableNullDeviceTimeout);
#pragma clang diagnostic pop
if(timedOut)
{
@ -111,10 +127,9 @@ bool BGMDeviceControlsList::MatchControlsListOf(AudioObjectID inDeviceID)
{
CAMutex::Locker locker(mMutex);
LazyInit();
if(mBGMDevice == kAudioObjectUnknown)
if(mBGMDevice.GetObjectID() != mAudioSystem.GetAudioDeviceForUID(CFSTR(kBGMDeviceUID)))
{
LogWarning("BGMDeviceControlsList::MatchControlsListOf: BGMDevice ID not set");
return false;
}
@ -228,13 +243,13 @@ void BGMDeviceControlsList::PropagateControlListChange()
{
CAMutex::Locker locker(mMutex);
LazyInit();
if(mBGMDevice == kAudioObjectUnknown)
if((mBGMDevice == kAudioObjectUnknown) || !mCanToggleDeviceOnSystem)
{
return;
}
InitDeviceToggling();
// Leave the default device alone if the user has changed it since launching BGMApp.
bool bgmDeviceIsDefault = true;
@ -267,17 +282,18 @@ void BGMDeviceControlsList::PropagateControlListChange()
#pragma mark Implementation
void BGMDeviceControlsList::LazyInit()
void BGMDeviceControlsList::InitDeviceToggling()
{
CAMutex::Locker locker(mMutex);
if(mInitialised)
if(mDeviceTogglingInitialised || !mCanToggleDeviceOnSystem)
{
return;
}
BGMAssert(mBGMDevice.GetObjectID() == mAudioSystem.GetAudioDeviceForUID(CFSTR(kBGMDeviceUID)),
"BGMDeviceControlsList::LazyInit: mBGMDevice device is not set to BGMDevice's ID");
"BGMDeviceControlsList::InitDeviceToggling: mBGMDevice device is not set to "
"BGMDevice's ID");
mDeviceToggleBlock = CreateDeviceToggleBlock();
mDeviceToggleBackBlock = CreateDeviceToggleBackBlock();
@ -285,8 +301,11 @@ void BGMDeviceControlsList::LazyInit()
// Register a listener to find out when the Null Device becomes available/unavailable. See
// ToggleDefaultDevice.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
dispatch_queue_attr_t attr =
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
#pragma clang diagnostic pop
mListenerQueue = dispatch_queue_create("com.bearisdriving.BGM.BGMDeviceControlsList", attr);
mListenerBlock = ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress* inAddresses) {
@ -302,35 +321,35 @@ void BGMDeviceControlsList::LazyInit()
switch(inAddresses[i].mSelector)
{
case kAudioHardwarePropertyDevices:
{
CAMutex::Locker innerLocker(mMutex);
{
CAMutex::Locker innerLocker(mMutex);
DebugMsg("BGMDeviceControlsList::LazyInit: Got "
"kAudioHardwarePropertyDevices");
DebugMsg("BGMDeviceControlsList::InitDeviceToggling: Got "
"kAudioHardwarePropertyDevices");
// Cancel the previous block in case it hasn't run yet.
DestroyBlock(mDeviceToggleBlock);
// Cancel the previous block in case it hasn't run yet.
DestroyBlock(mDeviceToggleBlock);
mDeviceToggleBlock = CreateDeviceToggleBlock();
mDeviceToggleBlock = CreateDeviceToggleBlock();
// Changing the default device too quickly after enabling the Null Device
// seems to cause problems with some programs. Not sure why.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kToggleDeviceInitialDelay),
dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
mDeviceToggleBlock);
}
// Changing the default device too quickly after enabling the Null Device
// seems to cause problems with some programs. Not sure why.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kToggleDeviceInitialDelay),
dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
mDeviceToggleBlock);
}
break;
}
}
};
BGMLogAndSwallowExceptions("BGMDeviceControlsList::LazyInit", [&] {
BGMLogAndSwallowExceptions("BGMDeviceControlsList::InitDeviceToggling", [&] {
mAudioSystem.AddPropertyListenerBlock(CAPropertyAddress(kAudioHardwarePropertyDevices),
mListenerQueue,
mListenerBlock);
});
mInitialised = true;
mDeviceTogglingInitialised = true;
}
void BGMDeviceControlsList::ToggleDefaultDevice()
@ -386,6 +405,9 @@ void BGMDeviceControlsList::SetNullDeviceEnabled(bool inEnabled)
(inEnabled ? kCFBooleanTrue : kCFBooleanFalse));
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
dispatch_block_t BGMDeviceControlsList::CreateDeviceToggleBlock()
{
return dispatch_block_create((dispatch_block_flags_t)0, ^{
@ -479,5 +501,7 @@ void BGMDeviceControlsList::DestroyBlock(dispatch_block_t __nullable & block)
}
}
#pragma clang diagnostic pop /* -Wpartial-availability */
#pragma clang assume_nonnull end

View file

@ -88,8 +88,8 @@ public:
#pragma mark Implementation
private:
/* This class is lazily initialised, mainly as a convenience for the unit tests. */
void LazyInit();
/*! Lazily initialises the fields used to toggle the default device. */
void InitDeviceToggling();
/*! Changes the OS X default audio device to the Null Device and then back to BGMDevice. */
void ToggleDefaultDevice();
/*!
@ -109,7 +109,9 @@ private:
private:
CAMutex mMutex { "Device Controls List" };
bool mInitialised = false;
bool mDeviceTogglingInitialised = false;
// OS X 10.9 doesn't have the functions we use for PropagateControlListChange.
bool mCanToggleDeviceOnSystem;
BGMAudioDevice mBGMDevice;
CAHALAudioSystemObject mAudioSystem; // Not guarded by the mutex.

View file

@ -20,7 +20,7 @@
# post_install.sh
# BGMXPCHelper
#
# Copyright © 2016 Kyle Neideck
# Copyright © 2016, 2017 Kyle Neideck
#
# Installs BGMXPCHelper's launchd plist file and "bootstraps" (registers/enables) it with launchd.
#
@ -28,30 +28,47 @@
# runs as the final build phase.
#
# Check the environment variables we need from Xcode.
if [[ -z ${EXECUTABLE_PATH} ]]; then
echo "Environment variable EXECUTABLE_PATH was not set." >&2
exit 1
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
# Check we have the paths we need, either in environment variables from Xcode or from the args.
if [[ -z $1 ]]; then
if [[ -z ${INSTALL_DIR} ]]; then
echo "Environment variable INSTALL_DIR was not set." >&2
exit 1
fi
else
INSTALL_DIR="$1"
fi
if [[ -z ${INSTALL_DIR} ]]; then
echo "Environment variable INSTALL_DIR was not set." >&2
exit 1
if [[ -z $2 ]]; then
if [[ -z ${EXECUTABLE_PATH} ]]; then
echo "Environment variable EXECUTABLE_PATH was not set." >&2
exit 1
fi
else
EXECUTABLE_PATH="$2"
fi
if [[ -z ${TARGET_BUILD_DIR} ]]; then
echo "Environment variable TARGET_BUILD_DIR was not set." >&2
exit 1
fi
if [[ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH} ]]; then
echo "Environment variable UNLOCALIZED_RESOURCES_FOLDER_PATH was not set." >&2
exit 1
if [[ -z $3 ]]; then
if [[ -z ${TARGET_BUILD_DIR} ]]; then
echo "Environment variable TARGET_BUILD_DIR was not set." >&2
exit 1
fi
if [[ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH} ]]; then
echo "Environment variable UNLOCALIZED_RESOURCES_FOLDER_PATH was not set." >&2
exit 1
fi
RESOURCES_PATH="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
RESOURCES_PATH="$3"
fi
# Safe mode.
set -euo pipefail
IFS=$'\n\t'
RESOURCES_PATH="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
# Show a warning if INSTALL_DIR isn't set to a safe installation directory.
if [[ $(bash "${RESOURCES_PATH}/safe_install_dir.sh" "${INSTALL_DIR}") != 1 ]]; then
echo "$(tput setaf 11)WARNING$(tput sgr0): Installing to \"${INSTALL_DIR}\" may be" \

View file

@ -20,7 +20,7 @@
# safe_install_dir.sh
# BGMXPCHelper
#
# Copyright © 2016 Kyle Neideck
# Copyright © 2016, 2017 Kyle Neideck
#
# Prints the path to a directory the BGMXPCHelper bundle can safely be installed to. Intended to be
# used as the INSTALL_DIR environment variable for xcodebuild commands. For example,
@ -56,6 +56,8 @@
# recommendation above, or "0" otherwise.
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
# Safe mode.
set -euo pipefail
IFS=$'\n\t'
@ -96,13 +98,17 @@ check_dir() {
# Used when we can't find a suitable installation directory. Prints an error message and exits.
# (Reaching this point should be very uncommon.)
fail() {
echo "$(tput setaf 11)WARNING$(tput sgr0): Installing BGMXPCHelper to its default location" \
"(${INSTALL_DIR} or, as a backup, ${BACKUP_INSTALL_DIR}) might not be secure on this" \
"system. It's recommended that each directory from the installation directory up to the" \
"root directory should be owned by root and not writable by any other user. See" \
"safe_install_dir.sh for more details." >&2
if [[ $ALLOW_UNSAFE_FALLBACK -eq 1 ]]; then
CONTINUE_ANYWAY="y"
else
echo "$(tput setaf 11)WARNING$(tput sgr0): Installing BGMXPCHelper to its default" \
"location (${INSTALL_DIR} or, as a backup, ${BACKUP_INSTALL_DIR}) might not be" \
"secure on this system. It's recommended that each directory from the installation" \
"directory up to the root directory should be owned by root and not writable by any" \
"other user. See safe_install_dir.sh for more details." >&2
read -e -p "Continue anyway? [y/N]" CONTINUE_ANYWAY
read -e -p "Continue anyway? [y/N]" CONTINUE_ANYWAY
fi
if [[ "${CONTINUE_ANYWAY}" == "y" ]] || [[ "${CONTINUE_ANYWAY}" == "Y" ]]; then
echo "${INSTALL_DIR}"
@ -114,16 +120,26 @@ fail() {
fi
}
# This script can be given a directory to check as an argument.
# (Uses "${1+x}" instead of "$1" because having our "safe mode" enabled makes the script fail if you
# reference an unset variable, even to check whether it's set or not.)
if [[ ! -z "${1+x}" ]]; then
# Check the given path exists and is a directory.
if [[ ! -d "$1" ]]; then echo "$1 is not a directory." >&2; exit 1; fi
ALLOW_UNSAFE_FALLBACK=0
check_dir "$1"
echo ${DIR_IS_SAFE}
exit 0
# This script can be given a directory to check as an argument, or the -y option, which tells this
# script to print the default dir if neither of the dirs are safe. The pkg installer uses -y so it
# can install anyway and show the user instructions to fix the permissions, rather than just
# failing.
#
# (This line uses "${1+x}" instead of "$1" because having our "safe mode" enabled makes the script
# fail if you reference an unset variable, even to check whether it's set or not.)
if [[ ! -z "${1+x}" ]]; then
if [[ "$1" == "-y" ]]; then
ALLOW_UNSAFE_FALLBACK=1
else
# Check the given path exists and is a directory.
if [[ ! -d "$1" ]]; then echo "$1 is not a directory." >&2; exit 1; fi
check_dir "$1"
echo ${DIR_IS_SAFE}
exit 0
fi
fi
# These are just for readability and to save keystrokes. If you change them, you'll have to change
@ -143,7 +159,7 @@ if [[ ! -e "${INSTALL_DIR}" ]]; then
sudo chmod go-w "${INSTALL_DIR}"
fi
# Check the directory Xcode installed the build to.
# Check the directory the build was installed to.
check_dir "${INSTALL_DIR}"
if [[ ${DIR_IS_SAFE} -eq 1 ]]; then

View file

@ -622,7 +622,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
WARNING_CFLAGS = "-Wpartial-availability";
@ -684,7 +684,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.9;
RUN_CLANG_STATIC_ANALYZER = YES;
WARNING_CFLAGS = "-Wpartial-availability";
};
@ -714,7 +714,6 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = BGMDriver/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.Driver;
@ -749,7 +748,6 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = BGMDriver/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.Driver;
PRODUCT_NAME = "$(TARGET_NAME)";

View file

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AudioServerPlugIn_MachServices</key>
<array>
<string>com.bearisdriving.BGM.XPCHelper</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
@ -15,11 +19,11 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>1.0.0</string>
<key>CFPlugInFactories</key>
<dict>
<key>C957AD43-DACA-4A40-8850-3BA8CE28FAF9</key>
@ -34,9 +38,5 @@
</dict>
<key>NSPrincipalClass</key>
<string></string>
<key>AudioServerPlugIn_MachServices</key>
<array>
<string>com.bearisdriving.BGM.XPCHelper</string>
</array>
</dict>
</plist>

View file

@ -19,7 +19,7 @@
#
# build_and_install.sh
#
# Copyright © 2016 Kyle Neideck
# Copyright © 2016, 2017 Kyle Neideck
# Copyright © 2016 Nick Jacques
#
# Builds and installs BGMApp, BGMDriver and BGMXPCHelper. Requires xcodebuild and Xcode.
@ -99,6 +99,8 @@ disable_error_handling() {
CONFIGURATION=Release
#CONFIGURATION=Debug
XCODEBUILD_ACTION="install"
# The default is to clean before installing because we want the log file to have roughly the same
# information after every build.
CLEAN=clean
@ -154,6 +156,7 @@ usage() {
echo "Usage: $0 [options]" >&2
echo -e "\t-n Don't clean before building/installing." >&2
echo -e "\t-d Debug build. (Release is the default.)" >&2
echo -e "\t-b Build only, don't install." >&2
echo -e "\t-w Ignore compiler warnings. (They're treated as errors by default.)" >&2
echo -e "\t-x [options] Extra options to pass to xcodebuild." >&2
echo -e "\t-c Continue on script errors. Might not be safe." >&2
@ -236,7 +239,7 @@ show_spinner() {
}
parse_options() {
while getopts ":ndwx:ch" opt; do
while getopts ":ndbwx:ch" opt; do
case $opt in
n)
CLEAN=""
@ -244,6 +247,13 @@ parse_options() {
d)
CONFIGURATION="Debug"
;;
b)
XCODEBUILD_ACTION="build"
# The dirs xcodebuild will build in.
APP_PATH="./BGMApp/build"
DRIVER_PATH="./BGMDriver/build"
XPC_HELPER_PATH="${APP_PATH}"
;;
w)
# TODO: What if they also pass their own OTHER_CFLAGS with -x?
XCODEBUILD_OPTIONS="${XCODEBUILD_OPTIONS} OTHER_CFLAGS=\"-Wno-error\""
@ -529,15 +539,20 @@ if [[ $(id -u) -eq 0 ]]; then
fi
# Print initial message.
echo "$(bold_face About to install Background Music). Please pause all audio, if you can."
[[ "${CONFIGURATION}" == "Debug" ]] && echo "Debug build."
echo
echo "This script will install:"
echo " - ${APP_PATH}/${APP_DIR}"
echo " - ${DRIVER_PATH}/${DRIVER_DIR}"
echo " - ${XPC_HELPER_PATH}/${XPC_HELPER_DIR}"
echo " - /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
echo
if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
echo "$(bold_face About to install Background Music). Please pause all audio, if you can."
[[ "${CONFIGURATION}" == "Debug" ]] && echo "Debug build."
echo
echo "This script will install:"
echo " - ${APP_PATH}/${APP_DIR}"
echo " - ${DRIVER_PATH}/${DRIVER_DIR}"
echo " - ${XPC_HELPER_PATH}/${XPC_HELPER_DIR}"
echo " - /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
echo
elif [[ "${XCODEBUILD_ACTION}" == "build" ]]; then
echo "$(bold_face Building Background Music...)"
echo
fi
# Make sure Xcode and the command line tools are installed and recent enough.
# This sets XCODE_VERSION to major.minor, e.g. 8.3, or -1 if Xcode isn't installed.
@ -545,11 +560,13 @@ XCODE_VERSION=$((${XCODEBUILD} -version 2>/dev/null || echo 'V -1') | head -n 1
check_xcode &
CHECK_XCODE_TASK_PID=$!
read -p "Continue (y/N)? " CONTINUE_INSTALLATION
if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
read -p "Continue (y/N)? " CONTINUE_INSTALLATION
if [[ "${CONTINUE_INSTALLATION}" != "y" ]] && [[ "${CONTINUE_INSTALLATION}" != "Y" ]]; then
echo "Installation cancelled."
exit 0
if [[ "${CONTINUE_INSTALLATION}" != "y" ]] && [[ "${CONTINUE_INSTALLATION}" != "Y" ]]; then
echo "Installation cancelled."
exit 0
fi
fi
# If the check_xcode process has already finished, we can check the result early.
@ -563,14 +580,16 @@ if ! is_alive ${CHECK_XCODE_TASK_PID}; then
enable_error_handling
fi
# Update the user's sudo timestamp. (Prompts the user for their password.)
# Don't call sudo -v if this is a Travis CI build.
if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then
echo "$(tput setaf 9)ERROR$(tput sgr0): This script must be run by a user with administrator" \
"(sudo) privileges." >&2
exit 1
if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
# Update the user's sudo timestamp. (Prompts the user for their password.)
# Don't call sudo -v if this is a Travis CI build.
if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then
echo "$(tput setaf 9)ERROR$(tput sgr0): This script must be run by a user with" \
"administrator (sudo) privileges." >&2
exit 1
fi
echo
fi
echo
while [[ ${NEED_TO_HANDLE_CHECK_XCODE_RESULT} -ne 0 ]]; do
disable_error_handling
@ -585,119 +604,127 @@ log_debug_info $*
# BGMDriver
echo "[1/3] Installing the virtual audio device $(bold_face ${DRIVER_DIR}) to" \
"$(bold_face ${DRIVER_PATH})." \
if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
SUDO="sudo"
ACTIONING="Installing"
else
SUDO=""
ACTIONING="Building"
fi
echo "[1/3] ${ACTIONING} the virtual audio device $(bold_face ${DRIVER_DIR}) to" \
"$(bold_face ${DRIVER_PATH})" \
| tee -a ${LOG_FILE}
# Disable the -e shell option and error trap for build commands so we can handle errors differently.
(disable_error_handling
# Build Apple's PublicUtility classes as a static library.
sudo "${XCODEBUILD}" -project BGMDriver/BGMDriver.xcodeproj \
-target "PublicUtility" \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
${XCODEBUILD_OPTIONS} \
${CLEAN} build >> ${LOG_FILE} 2>&1) &
${SUDO} "${XCODEBUILD}" -project BGMDriver/BGMDriver.xcodeproj \
-target "PublicUtility" \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
${XCODEBUILD_OPTIONS} \
${CLEAN} build >> ${LOG_FILE} 2>&1) &
(disable_error_handling
# Build and install BGMDriver
# TODO: Should these use -scheme instead?
sudo "${XCODEBUILD}" -project BGMDriver/BGMDriver.xcodeproj \
-target "Background Music Device" \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
${XCODEBUILD_OPTIONS} \
${CLEAN} install >> ${LOG_FILE} 2>&1) &
${SUDO} "${XCODEBUILD}" -project BGMDriver/BGMDriver.xcodeproj \
-target "Background Music Device" \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
${XCODEBUILD_OPTIONS} \
${CLEAN} "${XCODEBUILD_ACTION}" >> ${LOG_FILE} 2>&1) &
show_spinner "${BUILD_FAILED_ERROR_MSG}"
# BGMXPCHelper
echo "[2/3] Installing $(bold_face ${XPC_HELPER_DIR}) to $(bold_face ${XPC_HELPER_PATH})." \
echo "[2/3] ${ACTIONING} $(bold_face ${XPC_HELPER_DIR}) to $(bold_face ${XPC_HELPER_PATH})" \
| tee -a ${LOG_FILE}
(disable_error_handling
sudo "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
-target BGMXPCHelper \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
INSTALL_PATH="${XPC_HELPER_PATH}" \
${XCODEBUILD_OPTIONS} \
${CLEAN} install >> ${LOG_FILE} 2>&1) &
${SUDO} "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
-target BGMXPCHelper \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
INSTALL_PATH="${XPC_HELPER_PATH}" \
${XCODEBUILD_OPTIONS} \
${CLEAN} "${XCODEBUILD_ACTION}" >> ${LOG_FILE} 2>&1) &
show_spinner "${BUILD_FAILED_ERROR_MSG}"
# BGMApp
echo "[3/3] Installing $(bold_face ${APP_DIR}) to $(bold_face ${APP_PATH})." \
echo "[3/3] ${ACTIONING} $(bold_face ${APP_DIR}) to $(bold_face ${APP_PATH})" \
| tee -a ${LOG_FILE}
(disable_error_handling
sudo "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
-target "Background Music" \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
${XCODEBUILD_OPTIONS} \
${CLEAN} install >> ${LOG_FILE} 2>&1) &
${SUDO} "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
-target "Background Music" \
-configuration ${CONFIGURATION} \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
${XCODEBUILD_OPTIONS} \
${CLEAN} "${XCODEBUILD_ACTION}" >> ${LOG_FILE} 2>&1) &
show_spinner "${BUILD_FAILED_ERROR_MSG}"
# Fix Background Music.app owner/group.
#
# We have to run xcodebuild as root to install BGMXPCHelper because it installs to directories
# owned by root. But that means the build directory gets created by root, and since BGMApp uses the
# same build directory we have to run xcodebuild as root to install BGMApp as well.
#
# TODO: Can't we just chown -R the build dir before we install BGMApp? Then we wouldn't have to
# install BGMApp as root. (But maybe still handle the unlikely case of APP_PATH not being
# user-writable.)
sudo chown -R "$(whoami):admin" "${APP_PATH}/${APP_DIR}"
if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
# Fix Background Music.app owner/group.
#
# We have to run xcodebuild as root to install BGMXPCHelper because it installs to directories
# owned by root. But that means the build directory gets created by root, and since BGMApp uses
# the same build directory we have to run xcodebuild as root to install BGMApp as well.
#
# TODO: Can't we just chown -R the build dir before we install BGMApp? Then we wouldn't have to
# install BGMApp as root. (But maybe still handle the unlikely case of APP_PATH not being
# user-writable.)
sudo chown -R "$(whoami):admin" "${APP_PATH}/${APP_DIR}"
# Fix the build directories' owner/group. This is mainly so the whole source directory can be
# deleted easily after installing.
sudo chown -R "$(whoami):admin" "BGMApp/build" "BGMDriver/build"
# Fix the build directories' owner/group. This is mainly so the whole source directory can be
# deleted easily after installing.
sudo chown -R "$(whoami):admin" "BGMApp/build" "BGMDriver/build"
# Restart coreaudiod.
# Restart coreaudiod.
echo "Restarting coreaudiod to load the virtual audio device." \
| tee -a ${LOG_FILE}
echo "Restarting coreaudiod to load the virtual audio device." \
| tee -a ${LOG_FILE}
# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently
# some of these commands don't work with older versions of launchctl, so I figure there's no harm in
# trying a bunch of different ways (which should all work).
(sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
(sudo launchctl unload "${COREAUDIOD_PLIST}" &>/dev/null && \
sudo launchctl load "${COREAUDIOD_PLIST}" &>/dev/null) || \
sudo killall coreaudiod &>/dev/null) && \
sleep 5
# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently
# some of these commands don't work with older versions of launchctl, so I figure there's no
# harm in trying a bunch of different ways (which should all work).
(sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
(sudo launchctl unload "${COREAUDIOD_PLIST}" &>/dev/null && \
sudo launchctl load "${COREAUDIOD_PLIST}" &>/dev/null) || \
sudo killall coreaudiod &>/dev/null) && \
sleep 5
# Invalidate sudo ticket
sudo -k
# Invalidate sudo ticket
sudo -k
# Open BGMApp.
#
# I'd rather not open BGMApp here, or at least ask first, but you have to change your default audio
# device after restarting coreaudiod and this is the easiest way.
echo "Launching Background Music."
# Open BGMApp. We have to change the default audio device after restarting coreaudiod and this
# is the easiest way.
echo "Launching Background Music."
ERROR_MSG="${BGMAPP_FAILED_TO_START_ERROR_MSG}"
open "${APP_PATH}/${APP_DIR}"
ERROR_MSG="${BGMAPP_FAILED_TO_START_ERROR_MSG}"
open "${APP_PATH}/${APP_DIR}"
# Ignore script errors from this point.
disable_error_handling
# Ignore script errors from this point.
disable_error_handling
# Wait up to 5 seconds for Background Music to start.
(trap 'exit 1' TERM
while ! (ps -Ao ucomm= | grep 'Background Music' > /dev/null); do
sleep 1
done) &
show_spinner "${BGMAPP_FAILED_TO_START_ERROR_MSG}" 5
# Wait up to 5 seconds for Background Music to start.
(trap 'exit 1' TERM
while ! (ps -Ao ucomm= | grep 'Background Music' > /dev/null); do
sleep 1
done) &
show_spinner "${BGMAPP_FAILED_TO_START_ERROR_MSG}" 5
fi
echo "Done."

138
package.sh Executable file
View file

@ -0,0 +1,138 @@
#!/bin/bash
# vim: tw=100:
# This file is part of Background Music.
#
# Background Music 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 2 of the
# License, or (at your option) any later version.
#
# Background Music 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 Background Music. If not, see <http://www.gnu.org/licenses/>.
#
# package.sh
#
# Copyright © 2017 Kyle Neideck
# Copyright © 2016, 2017 Takayama Fumihiko
#
# Build Background Music and package it into a .pkg file and a .zip of the debug symbols (dSYM).
#
# Based on https://github.com/tekezo/Karabiner-Elements/blob/master/make-package.sh
#
# TODO: Code signing. See `man pkgbuild`.
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"; export PATH
# Sets all dirs in $1 to 755 (rwxr-xr-x) and all files in $1 to 644 (rw-r--r--).
set_permissions() {
find "$1" -print0 | while read -d $'\0' filepath; do
filename="${filepath##*/}"
if [ -d "$filepath" ]; then
chmod -h 755 "$filepath"
else
chmod -h 644 "$filepath"
fi
done
}
# --------------------------------------------------
# Build
bash build_and_install.sh -b
version="$(/usr/libexec/PlistBuddy \
-c "Print CFBundleShortVersionString" \
"BGMApp/build/Release/Background Music.app/Contents/Info.plist")"
out_dir="Background-Music-$version"
rm -rf "$out_dir"
mkdir "$out_dir"
# Separate the debug symbols and the .app
echo "Archiving debug symbols"
dsym_archive="$out_dir/Background Music.dSYM-$version.zip"
mv "BGMApp/build/Release/Background Music.app/Contents/MacOS/Background Music.dSYM" \
"Background Music.dSYM"
zip -r "$dsym_archive" "Background Music.dSYM"
rm -r "Background Music.dSYM"
# --------------------------------------------------
echo "Copying Files"
rm -rf "pkgroot"
mkdir -p "pkgroot"
mkdir -p "pkgroot/Library/Audio/Plug-Ins/HAL"
cp -R "BGMDriver/build/Release/Background Music Device.driver" "pkgroot/Library/Audio/Plug-Ins/HAL/"
mkdir -p "pkgroot/Applications"
cp -R "BGMApp/build/Release/Background Music.app" "pkgroot/Applications"
scripts_dir="$(mktemp -d)"
cp "pkg/preinstall" "$scripts_dir"
cp "pkg/postinstall" "$scripts_dir"
cp "BGMApp/BGMXPCHelper/com.bearisdriving.BGM.XPCHelper.plist.template" "$scripts_dir"
cp "BGMApp/BGMXPCHelper/safe_install_dir.sh" "$scripts_dir"
cp "BGMApp/BGMXPCHelper/post_install.sh" "$scripts_dir"
cp -R "BGMApp/build/Release/BGMXPCHelper.xpc" "$scripts_dir"
set_permissions "pkgroot"
chmod 755 "pkgroot/Applications/Background Music.app/Contents/MacOS/Background Music"
chmod 755 "pkgroot/Library/Audio/Plug-Ins/HAL/Background Music Device.driver/Contents/MacOS/Background Music Device"
set_permissions "$scripts_dir"
chmod 755 "$scripts_dir/preinstall"
chmod 755 "$scripts_dir/postinstall"
chmod 755 "$scripts_dir/BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper"
rm -rf "pkgres"
mkdir -p "pkgres"
cp "Images/FermataIcon.pdf" "pkgres"
sed "s/{{VERSION}}/$version/g" "pkg/Distribution.xml.template" > "pkg/Distribution.xml"
# --------------------------------------------------
pkg="$out_dir/BackgroundMusic-$version.pkg"
pkg_identifier="com.bearisdriving.BGM"
echo "Creating $pkg"
pkgbuild \
--root "pkgroot" \
--component-plist "pkg/pkgbuild.plist" \
--identifier "$pkg_identifier" \
--scripts "$scripts_dir" \
--version "$version" \
--install-location "/" \
"$out_dir/Installer.pkg"
productbuild \
--distribution "pkg/Distribution.xml" \
--resources "pkgres" \
--package-path "$out_dir" \
"$pkg"
rm -f "$out_dir/Installer.pkg"
rm -rf "pkgroot"
rm -rf "pkgres"
rm -f "pkg/Distribution.xml"
# Print checksums
echo "MD5 checksums:"
md5 {"$pkg","$dsym_archive"}
echo "SHA256 checksums:"
shasum -a 256 {"$pkg","$dsym_archive"}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<installer-gui-script minSpecVersion="1">
<title>Background Music {{VERSION}}</title>
<volume-check>
<allowed-os-versions>
<!-- TODO: Get this from the Xcode project files instead of hardcoding it. -->
<os-version min="10.9" />
</allowed-os-versions>
</volume-check>
<!--
Do not specify <domains>.
Installer does not show "OS X version X.Y.Z or later is required" message when <domains> exists.
<domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true" />
-->
<pkg-ref id="com.bearisdriving.BGM"/>
<options customize="never" require-scripts="false" />
<choices-outline>
<line choice="default">
<line choice="com.bearisdriving.BGM" />
</line>
</choices-outline>
<choice id="default"/>
<choice id="com.bearisdriving.BGM" visible="false">
<pkg-ref id="com.bearisdriving.BGM" />
</choice>
<pkg-ref id="com.bearisdriving.BGM" version="{{VERSION}}">Installer.pkg</pkg-ref>
<pkg-ref id="com.bearisdriving.BGM">
<must-close>
<app id="com.bearisdriving.BGM.App" />
</must-close>
</pkg-ref>
<background file="FermataIcon.pdf" alignment="bottomleft" />
</installer-gui-script>

14
pkg/pkgbuild.plist Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>RootRelativeBundlePath</key> <string>Applications/Background Music.app</string>
<key>BundleHasStrictIdentifier</key> <false/>
<key>BundleIsRelocatable</key> <false/>
<key>BundleIsVersionChecked</key> <false/>
<key>BundleOverwriteAction</key> <string>upgrade</string>
</dict>
</array>
</plist>

55
pkg/postinstall Executable file
View file

@ -0,0 +1,55 @@
#!/bin/sh
# vim: tw=100:
# This file is part of Background Music.
#
# Background Music 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 2 of the
# License, or (at your option) any later version.
#
# Background Music 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 Background Music. If not, see <http://www.gnu.org/licenses/>.
#
# preinstall
#
# Copyright © 2017 Kyle Neideck
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
dest_volume="$3"
xpc_helper_path="$(bash safe_install_dir.sh -y)"
logger "Installing BGMXPCHelper to $xpc_helper_path"
cp -Rf "BGMXPCHelper.xpc" "$xpc_helper_path"
# TODO: Fail the install and show an error message if this fails.
bash "post_install.sh" "$xpc_helper_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper" "."
# TODO: Verify the installed files, their permissions, the _BGMXPCHelper user/group, etc.
# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently
# some of these commands don't work with older versions of launchctl, so I figure there's no
# harm in trying a bunch of different ways (which should all work).
(launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
(launchctl unload "$coreaudiod_plist" &>/dev/null && \
launchctl load "$coreaudiod_plist" &>/dev/null) || \
killall coreaudiod &>/dev/null) && \
sleep 5
open "${dest_volume}/Applications/Background Music.app"
# The installer plays a sound when it finishes, so give BGMApp a second to launch.
sleep 1

55
pkg/preinstall Executable file
View file

@ -0,0 +1,55 @@
#!/bin/sh
# vim: tw=100:
# This file is part of Background Music.
#
# Background Music 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 2 of the
# License, or (at your option) any later version.
#
# Background Music 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 Background Music. If not, see <http://www.gnu.org/licenses/>.
#
# preinstall
#
# Copyright © 2017 Kyle Neideck
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
# Show a warning if we can't find a safe dir to install BGMXPCHelper in. Should be very unlikely.
xpc_helper_path="$(bash safe_install_dir.sh -y)"
if [ "$(bash safe_install_dir.sh "$xpc_helper_path")" != "1" ]; then
# TODO: This message could be more user friendly.
msg="We need to install a helper app called BGMXPCHelper to ${xpc_helper_path}, but it may not "
msg+="be secure to do so.\n\n"
msg+="${xpc_helper_path} and all of its parent directories should be owned by 'root', with the "
msg+="group 'wheel', and have permissions 755 (rwxr-xr-x).\n\n"
msg+="Background Music will still work if you decide to continue."
if ! osascript <<EOT
tell application id "com.apple.systemuiserver"
display alert "Warning" \
message "$msg" \
buttons {"Cancel", "Install Anyway"} \
default button "Cancel" \
cancel button "Cancel" \
as warning
end tell
EOT
then
exit 1
fi
fi
exit 0