Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
This commit is contained in:
Derrick Stolee 2020-10-27 12:19:48 -04:00
Родитель a82cef1201
Коммит 8aae90168c
74 изменённых файлов: 4 добавлений и 2980 удалений

Просмотреть файл

@ -14,8 +14,6 @@
<ItemGroup>
<ProjectReference Include="..\Scalar\Scalar.csproj" />
<ProjectReference Include="..\Scalar.Service\Scalar.Service.csproj" />
<ProjectReference Include="..\Scalar.Notifications.Mac\Scalar.Notifications.Mac.csproj" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -38,8 +38,6 @@ fi
LOCALBIN_DIR="${OUT_DIR}/usr/local/bin"
SCALAR_DESTINATION="${OUT_DIR}/usr/local/scalar"
AGENTPLIST_DESTINATION="${OUT_DIR}/Library/LaunchAgents"
LIBRARYAPPSUPPORT_DESTINATION="${OUT_DIR}/Library/Application Support/Scalar"
function CreateLayoutDirectories()
{
@ -47,7 +45,7 @@ function CreateLayoutDirectories()
cleanLayout="rm -rf \"${OUT_DIR}\""
eval $cleanLayout || exit 1
mkdirLayout="mkdir -p \"${LOCALBIN_DIR}\" \"${SCALAR_DESTINATION}\" \"${AGENTPLIST_DESTINATION}\" \"${LIBRARYAPPSUPPORT_DESTINATION}\""
mkdirLayout="mkdir -p \"${LOCALBIN_DIR}\" \"${SCALAR_DESTINATION}\""
eval $mkdirLayout || exit 1
}
@ -83,48 +81,11 @@ function CopyUninstaller()
eval $copyUninstaller || exit 1
}
function CopyNativeApp()
{
SCALAR_NOTIFICATION_APP="${BIN_DIR}/Scalar.Notifications.Mac/bin/${CONFIGURATION}/native/${RUNTIMEIDENTIFIER}/Scalar.app"
if [ ! -d "$SCALAR_NOTIFICATION_APP" ] ; then
echo "Error: Could not find native notification app at ${SCALAR_NOTIFICATION_APP}."
exit 1
fi
copyNotificationApp="cp -Rf \"${SCALAR_NOTIFICATION_APP}\" \"${LIBRARYAPPSUPPORT_DESTINATION}\""
eval $copyNotificationApp || exit 1
}
function CopyAgentPlists()
{
NOTIFICATION_PLIST_PATH="${SRC_DIR}/Scalar.Notifications.Mac/org.scalar.usernotification.plist"
SERVICE_PLIST_PATH="${SRC_DIR}/Scalar.Service/Mac/org.scalar.service.plist"
if [ ! -f "$NOTIFICATION_PLIST_PATH" ] ; then
echo "Error: Could not find notification app agent plist at ${NOTIFICATION_PLIST_PATH}."
exit 1
fi
if [ ! -f "$SERVICE_PLIST_PATH" ] ; then
echo "Error: Could not find service agent plist at ${SERVICE_PLIST_PATH}."
exit 1
fi
copyNotificationPlist="cp -Rf \"${NOTIFICATION_PLIST_PATH}\" \"${AGENTPLIST_DESTINATION}/.\""
eval $copyNotificationPlist || exit 1
copyServicePlist="cp -Rf \"${SERVICE_PLIST_PATH}\" \"${AGENTPLIST_DESTINATION}/.\""
eval $copyServicePlist || exit 1
}
function Run()
{
CreateLayoutDirectories
CopyScalar
CopyUninstaller
CopyNativeApp
CopyAgentPlists
}
Run

Просмотреть файл

@ -50,7 +50,7 @@ function CreateScalarInstaller()
mkdirOutDir="mkdir -p \"${OUT_DIR}\" \"${COMPONENTS_DIR}\""
eval $mkdirOutDir || exit 1
pkgBuildCommand="/usr/bin/pkgbuild --identifier $INSTALLER_PACKAGE_ID --component-plist \"${COMPONENTSPLIST_PATH}\" --scripts \"${SCRIPTS_DIR}\" --root \"${LAYOUT_DIR}\" \"${COMPONENTS_DIR}/${COMPONENT_PACKAGE_NAME}\""
pkgBuildCommand="/usr/bin/pkgbuild --identifier $INSTALLER_PACKAGE_ID --scripts \"${SCRIPTS_DIR}\" --root \"${LAYOUT_DIR}\" \"${COMPONENTS_DIR}/${COMPONENT_PACKAGE_NAME}\""
echo $pkgBuildCommand
eval $pkgBuildCommand || exit 1
}

Просмотреть файл

@ -1,18 +0,0 @@
<?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>BundleHasStrictIdentifier</key>
<true/>
<key>BundleIsRelocatable</key>
<false/>
<key>BundleIsVersionChecked</key>
<true/>
<key>BundleOverwriteAction</key>
<string>upgrade</string>
<key>RootRelativeBundlePath</key>
<string>Library/Application Support/Scalar/Scalar.app</string>
</dict>
</array>
</plist>

Просмотреть файл

@ -1,37 +0,0 @@
#!/bin/bash
function startOrRestartService()
{
domain=$1
service=$2
if [[ $domain == system* ]]; then
plistPath="/Library/LaunchDaemons"
elif [[ $domain == gui/* ]]; then
plistPath="/Library/LaunchAgents"
fi
startCmd="/bin/launchctl bootstrap $domain $plistPath/$service.plist"
restartCmd="/bin/launchctl kickstart -k $domain/$service"
isLoaded=`/bin/launchctl print $domain/$service | wc -l`
if [ $isLoaded -gt "0" ]; then
echo "Restarting Service: '$restartCmd'"
eval $restartCmd || exit 1
else
echo "Starting Service: '$startCmd'"
eval $startCmd || exit 1
fi
}
# Load Launch Agents in all active User sessions
# There will be one loginwindow instance for each logged in user,
# get its uid (this will correspond to the logged in user's id.)
# Then use launchctl bootstrap gui/uid to auto load the Service
# for each user.
declare -a launchAgents=(
"org.scalar.usernotification"
"org.scalar.service"
)
for uid in $(ps -Ac -o uid,command | grep -iw "Finder" | awk '{print $1}'); do
for nextLaunchAgent in "${launchAgents[@]}"; do
startOrRestartService "gui/$uid" $nextLaunchAgent
done
done

Просмотреть файл

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.Build.NoTargets">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
</PropertyGroup>
<Target Name="_Clean" AfterTargets="Clean">
<RemoveDir Directories="$(ProjectOutPath)" />
</Target>
<!--
Only build and test the native bits when running on macOS.
-->
<Target Name="_XcodeBuild" AfterTargets="Publish" Condition="'$(OSPlatform)' == 'osx'">
<Exec Command="$(MSBuildProjectDirectory)\build.sh $(Configuration) '$(ProjectOutPath)xcodebuild' '$(ProjectOutPath)bin\$(Configuration)\native\$(RuntimeIdentifier)' $(Version)" />
<Exec Command="$(MSBuildProjectDirectory)\test.sh $(Configuration) '$(ProjectOutPath)xcodebuild.test'" />
</Target>
</Project>

Просмотреть файл

@ -1,22 +0,0 @@
<?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">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

Просмотреть файл

@ -1,8 +0,0 @@
#import <Foundation/Foundation.h>
#import "ScalarAboutWindowController.h"
@interface ScalarMockAboutWindowController : ScalarAboutWindowController
@property (readonly) BOOL aboutBoxDisplayed;
@end

Просмотреть файл

@ -1,23 +0,0 @@
#import <Foundation/Foundation.h>
#import "ScalarMockAboutWindowController.h"
@interface ScalarMockAboutWindowController()
@property (readwrite) BOOL aboutBoxDisplayed;
@end
@implementation ScalarMockAboutWindowController
- (instancetype) initWithProductInfo:(ScalarProductInfoFetcher *) productInfo
{
self = [super initWithProductInfoFetcher:productInfo];
return self;
}
- (IBAction)showWindow:(nullable id)sender
{
self.aboutBoxDisplayed = YES;
}
@end

Просмотреть файл

@ -1,7 +0,0 @@
#import <Foundation/Foundation.h>
@interface ScalarMockNotificationCenter : NSUserNotificationCenter
@property (assign) BOOL notificationDelivered;
@end

Просмотреть файл

@ -1,31 +0,0 @@
#import <Foundation/Foundation.h>
#import "ScalarMockNotificationCenter.h"
@interface ScalarMockNotificationCenter ()
@property (strong) NSUserNotification *expectedNotification;
@end
@implementation ScalarMockNotificationCenter
- (instancetype) initWithExpectedNotification:(NSUserNotification *) notification
{
if (self = [super init])
{
_expectedNotification = notification;
}
return self;
}
- (void)deliverNotification:(NSUserNotification *) notification
{
if ([notification.title isEqualToString:self.expectedNotification.title] &&
[notification.informativeText isEqualToString:self.expectedNotification.informativeText])
{
self.notificationDelivered = YES;
}
}
@end

Просмотреть файл

@ -1,13 +0,0 @@
#import <Foundation/Foundation.h>
#import "ScalarProductInfoFetcher.h"
NS_ASSUME_NONNULL_BEGIN
@interface ScalarMockProductInfoFetcher : ScalarProductInfoFetcher
- (instancetype _Nullable) initWithGitVersion:(NSString *) gitVersion
scalarVersion:(NSString *) scalarVersion;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,39 +0,0 @@
#import <Foundation/Foundation.h>
#import "ScalarMockProductInfoFetcher.h"
@interface ScalarMockProductInfoFetcher()
@property (copy) NSString *gitVersion;
@property (copy) NSString *scalarVersion;
@end
@implementation ScalarMockProductInfoFetcher
- (instancetype) initWithGitVersion:(NSString *) gitVersion
scalarVersion:(NSString *) scalarVersion
{
if (self = [super init])
{
_gitVersion = [gitVersion copy];
_scalarVersion = [scalarVersion copy];
}
return self;
}
- (BOOL) tryGetScalarVersion:(NSString *__autoreleasing *) version
error:(NSError *__autoreleasing *) error
{
*version = self.scalarVersion;
return YES;
}
- (BOOL) tryGetGitVersion:(NSString *__autoreleasing *) version
error:(NSError *__autoreleasing *) error
{
*version = self.gitVersion;
return YES;
}
@end

Просмотреть файл

@ -1,47 +0,0 @@
#import <XCTest/XCTest.h>
#import "ScalarMockAboutWindowController.h"
#import "ScalarMockProductInfoFetcher.h"
NSString * const ExpectedGitVersionString = @"2.20.1.vfs.1.1.104.g2ab7360";
NSString * const ExpectedScalarVersionString = @"1.0.19116.1";
@interface ScalarAboutWindowControllerTests : XCTestCase
@property (strong) ScalarAboutWindowController *windowController;
@end
@implementation ScalarAboutWindowControllerTests
- (void)setUp
{
[super setUp];
ScalarMockProductInfoFetcher *mockProductInfoFetcher =
[[ScalarMockProductInfoFetcher alloc] initWithGitVersion:(NSString *) ExpectedGitVersionString
scalarVersion:(NSString *) ExpectedScalarVersionString];
self.windowController = [[ScalarAboutWindowController alloc]
initWithProductInfoFetcher:mockProductInfoFetcher];
}
- (void)tearDown
{
[super tearDown];
}
- (void)testAboutWindowContainsScalarVersion
{
XCTAssertEqual(self.windowController.scalarVersion,
ExpectedScalarVersionString,
@"Incorrect Scalar version displayed in About box");
}
- (void)testAboutWindowContainsGitVersion
{
XCTAssertEqual(self.windowController.gitVersion,
ExpectedGitVersionString,
@"Incorrect Git version displayed in About box");
}
@end

Просмотреть файл

@ -1,115 +0,0 @@
#import <XCTest/XCTest.h>
#import "ScalarMessageParser.h"
@interface MessageParserTests : XCTestCase
@end
@implementation MessageParserTests
- (void)testParsingValidMessageSucceeds
{
NSDictionary *expectedDict = [self validMessage];
NSData *messageData =
[NSJSONSerialization dataWithJSONObject:expectedDict
options:NSJSONWritingPrettyPrinted
error:nil];
NSError *error;
NSDictionary *parsedMessage;
XCTAssertTrue([ScalarMessageParser tryParseData:messageData
message:&parsedMessage
error:&error]);
XCTAssertNil(error);
[self validateParsedMessage:parsedMessage expectedMessage:expectedDict];
}
- (void)testParsingMessageWithTrailingCtrlCharsSucceed
{
NSDictionary *expectedDict = [self validMessage];
NSData *messageData =
[NSJSONSerialization dataWithJSONObject:expectedDict
options:NSJSONWritingPrettyPrinted
error:nil];
NSMutableData *dataWithCtrlChars = [NSMutableData dataWithData:messageData];
NSString *stringWithCtrlChars = [NSString stringWithFormat:@"%c%c%c%c%c%c%c",
0x07,
0x08,
0x1B,
0x0C,
0x0A,
0x0D,
0x09];
[dataWithCtrlChars appendData:[stringWithCtrlChars
dataUsingEncoding:NSUTF8StringEncoding]];
NSError *error;
NSDictionary *parsedMessage;
XCTAssertTrue([ScalarMessageParser tryParseData:dataWithCtrlChars
message:&parsedMessage
error:&error]);
XCTAssertNil(error);
[self validateParsedMessage:parsedMessage expectedMessage:expectedDict];
}
- (void)testParsingMalformedMessageFails
{
NSString *message = @"{ \"Id\", \"Message\", \"Foobar\"}";
NSError *error;
NSDictionary *parsedMessage;
XCTAssertFalse([ScalarMessageParser tryParseData:[message dataUsingEncoding:NSUTF8StringEncoding]
message:&parsedMessage
error:&error]);
XCTAssertNil(parsedMessage);
XCTAssertNotNil(error);
}
- (void)testParsingEmptyMessageFails
{
NSString *message = @"";
NSError *error;
NSDictionary *parsedMessage;
XCTAssertFalse([ScalarMessageParser tryParseData:[message dataUsingEncoding:NSUTF8StringEncoding]
message:&parsedMessage
error:&error]);
XCTAssertNil(parsedMessage);
XCTAssertNotNil(error);
}
#pragma mark Utility Methods
- (NSDictionary *)validMessage
{
NSInteger messageId = 1;
NSString *title = @"Scalar Mount";
NSString *message = @"Successfully mount repo";
NSString *enlistment = @"/Users/foo/bar";
NSInteger enlistmentCount = 0;
NSDictionary *validDict = @{
@"Id" : [NSNumber numberWithLong:messageId],
@"Title" : title,
@"Message" : message,
@"Enlistment" : enlistment,
@"EnlistmentCount" : [NSNumber numberWithLong:enlistmentCount]
};
return validDict;
}
- (BOOL)validateParsedMessage:(NSDictionary *)messageDict
expectedMessage:(NSDictionary *)expectedDict
{
XCTAssertNotNil(messageDict, @"Parse error: failure parsing message");
[messageDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key,
id _Nonnull obj,
BOOL * _Nonnull stop)
{
XCTAssertEqualObjects(obj,
expectedDict[key],
@"Parse error: mismatch in values of %@",
key);
}];
}
@end

Просмотреть файл

@ -1,93 +0,0 @@
#import <XCTest/XCTest.h>
#import "ScalarNotification.h"
@interface ScalarNotificationTests : XCTestCase
@end
@implementation ScalarNotificationTests
- (void)testCreateNotificationWithMissingIdFails
{
NSDictionary *message = @{
@"Title" : @"foo",
@"Message" : @"bar",
@"Enlistment" : @"/foo/bar",
@"EnlistmentCount" : [NSNumber numberWithLong:0]
};
NSError *error;
ScalarNotification *notification;
XCTAssertFalse([ScalarNotification tryValidateMessage:message
buildNotification:&notification
error:&error]);
XCTAssertNotNil(error);
}
- (void)testCreateNotificationWithInvalidIdFails
{
NSDictionary *message = @{
@"Id" : [NSNumber numberWithLong:32],
@"Title" : @"foo",
@"Message" : @"bar",
@"EnlistmentCount" : [NSNumber numberWithLong:0]
};
NSError *error;
ScalarNotification *notification;
XCTAssertFalse([ScalarNotification tryValidateMessage:message
buildNotification:&notification
error:&error]);
XCTAssertNotNil(error);
}
- (void)testCreateAutomountNotificationWithValidMessageSucceeds
{
NSDictionary *message = @{
@"Id" : [NSNumber numberWithLong:0],
@"EnlistmentCount" : [NSNumber numberWithLong:5]
};
NSError *error;
ScalarNotification *notification;
XCTAssertTrue([ScalarNotification tryValidateMessage:message
buildNotification:&notification
error:&error]);
XCTAssertTrue([notification.title isEqualToString:@"Scalar AutoMount"]);
XCTAssertTrue([notification.message isEqualToString:@"Attempting to mount 5 Scalar repos(s)"]);
XCTAssertNil(error);
}
- (void)testCreateMountNotificationWithValidMessageSucceeds
{
NSString *enlistment = @"/Users/foo/bar/foo.bar";
NSDictionary *message = @{
@"Id" : [NSNumber numberWithLong:1],
@"Enlistment" : enlistment
};
NSError *error;
ScalarNotification *notification;
XCTAssertTrue([ScalarNotification tryValidateMessage:message
buildNotification:&notification
error:&error]);
XCTAssertTrue([notification.title isEqualToString:@"Scalar AutoMount"]);
XCTAssertTrue([notification.message containsString:enlistment]);
XCTAssertNil(error);
}
- (void)testCreateMountNotificationWithMissingEnlistmentFails
{
NSDictionary *message = @{
@"Id" : [NSNumber numberWithLong:1],
};
NSError *error;
ScalarNotification *notification;
XCTAssertFalse([ScalarNotification tryValidateMessage:message
buildNotification:&notification
error:&error]);
XCTAssertNotNil(error);
}
@end

Просмотреть файл

@ -1,56 +0,0 @@
#import <XCTest/XCTest.h>
#import "ScalarMockAboutWindowController.h"
#import "ScalarMockProductInfoFetcher.h"
#import "ScalarStatusBarItem.h"
NSString * const ExpectedAboutMenuTitle = @"About Scalar";
@interface ScalarStatusBarItemTests : XCTestCase
@property (strong) ScalarStatusBarItem *statusbarItem;
@property (strong) ScalarMockAboutWindowController *aboutWindowController;
@end
@implementation ScalarStatusBarItemTests
- (void)setUp
{
[super setUp];
ScalarMockProductInfoFetcher *mockProductInfoFetcher = [[ScalarMockProductInfoFetcher alloc]
initWithGitVersion:@""
scalarVersion:@""];
self.aboutWindowController = [[ScalarMockAboutWindowController alloc]
initWithProductInfoFetcher:mockProductInfoFetcher];
self.statusbarItem = [[ScalarStatusBarItem alloc]
initWithAboutWindowController:self.aboutWindowController];
[self.statusbarItem load];
}
- (void)tearDown
{
[super tearDown];
}
- (void)testStatusItemContainsAboutMenu
{
NSMenu *statusMenu = [self.statusbarItem getStatusMenu];
XCTAssertNotNil(statusMenu, @"Status bar does not contain Scalar menu");
NSMenuItem *menuItem = [statusMenu itemWithTitle:ExpectedAboutMenuTitle];
XCTAssertNotNil(menuItem, @"Missing \"%@\" item in Scalar menu", ExpectedAboutMenuTitle);
}
- (void)testAboutMenuClickDisplaysAboutBox
{
[self.statusbarItem handleMenuClick:nil];
XCTAssertTrue(self.aboutWindowController.aboutBoxDisplayed,
@"Clicking on \"%@\" menu does not show About box",
ExpectedAboutMenuTitle);
}
@end

Просмотреть файл

@ -1,593 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
433FE819227B593500488730 /* ScalarAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE818227B593500488730 /* ScalarAppDelegate.m */; };
433FE81B227B594300488730 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 433FE81A227B594300488730 /* Assets.xcassets */; };
433FE81E227B594300488730 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 433FE81C227B594300488730 /* MainMenu.xib */; };
433FE821227B594300488730 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE820227B594300488730 /* main.m */; };
433FE8562280C21000488730 /* ScalarStatusBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE8542280C21000488730 /* ScalarStatusBarItem.m */; };
433FE85A2280E10F00488730 /* ScalarProductInfoFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE8592280E10F00488730 /* ScalarProductInfoFetcher.m */; };
433FE8632281D06100488730 /* ScalarAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE8602281D06000488730 /* ScalarAboutWindowController.m */; };
433FE8642281D06100488730 /* ScalarAboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 433FE8612281D06100488730 /* ScalarAboutWindowController.xib */; };
43583C362289BDAA003357D6 /* ScalarStatusBarItemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43583C352289BDAA003357D6 /* ScalarStatusBarItemTests.m */; };
43583C382289BE94003357D6 /* ScalarAboutWindowControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43583C372289BE94003357D6 /* ScalarAboutWindowControllerTests.m */; };
435B1E0F228CC1E000E853F3 /* ScalarProcessRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1E0E228CC1E000E853F3 /* ScalarProcessRunner.m */; };
43AF0DF822B01C9800E54D48 /* ScalarNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0DF422B01C9700E54D48 /* ScalarNotification.m */; };
43AF0DFA22B01C9800E54D48 /* ScalarMessageParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0DF522B01C9700E54D48 /* ScalarMessageParser.m */; };
43AF0DFC22B01C9800E54D48 /* ScalarMessageListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0DF622B01C9800E54D48 /* ScalarMessageListener.m */; };
43AF0E0222B01E3200E54D48 /* ScalarNotificationDisplay.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0E0122B01E3200E54D48 /* ScalarNotificationDisplay.m */; };
43AF0E0322B023BE00E54D48 /* ScalarMockAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1DFA228B4C8400E853F3 /* ScalarMockAboutWindowController.m */; };
43AF0E0422B023BE00E54D48 /* ScalarMockProductInfoFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1DFE228B590100E853F3 /* ScalarMockProductInfoFetcher.m */; };
43AF0E0522B023BE00E54D48 /* ScalarMockNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1E01228B6C3900E853F3 /* ScalarMockNotificationCenter.m */; };
43AF0E0822B023F200E54D48 /* ScalarMessageParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0E0622B023F200E54D48 /* ScalarMessageParserTests.m */; };
43AF0E0922B023F200E54D48 /* ScalarNotificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0E0722B023F200E54D48 /* ScalarNotificationTests.m */; };
43C021F022B18BDD00F868F2 /* ScalarNotificationErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C021EF22B18BDD00F868F2 /* ScalarNotificationErrors.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
43583C302285E3E1003357D6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 433FE80C227B593500488730 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 433FE813227B593500488730;
remoteInfo = Scalar;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
433FE814227B593500488730 /* Scalar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Scalar.app; sourceTree = BUILT_PRODUCTS_DIR; };
433FE817227B593500488730 /* ScalarAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarAppDelegate.h; sourceTree = "<group>"; };
433FE818227B593500488730 /* ScalarAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarAppDelegate.m; sourceTree = "<group>"; };
433FE81A227B594300488730 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
433FE81D227B594300488730 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
433FE81F227B594300488730 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
433FE820227B594300488730 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
433FE822227B594300488730 /* Scalar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Scalar.entitlements; sourceTree = "<group>"; };
433FE8532280C21000488730 /* ScalarStatusBarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarStatusBarItem.h; sourceTree = "<group>"; };
433FE8542280C21000488730 /* ScalarStatusBarItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarStatusBarItem.m; sourceTree = "<group>"; };
433FE8582280E10F00488730 /* ScalarProductInfoFetcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarProductInfoFetcher.h; sourceTree = "<group>"; };
433FE8592280E10F00488730 /* ScalarProductInfoFetcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarProductInfoFetcher.m; sourceTree = "<group>"; };
433FE8602281D06000488730 /* ScalarAboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarAboutWindowController.m; sourceTree = "<group>"; };
433FE8612281D06100488730 /* ScalarAboutWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ScalarAboutWindowController.xib; sourceTree = "<group>"; };
433FE8622281D06100488730 /* ScalarAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarAboutWindowController.h; sourceTree = "<group>"; };
43583C2B2285E3E1003357D6 /* Scalar.UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Scalar.UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
43583C2F2285E3E1003357D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
43583C352289BDAA003357D6 /* ScalarStatusBarItemTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarStatusBarItemTests.m; sourceTree = "<group>"; };
43583C372289BE94003357D6 /* ScalarAboutWindowControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarAboutWindowControllerTests.m; sourceTree = "<group>"; };
435B1DFA228B4C8400E853F3 /* ScalarMockAboutWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarMockAboutWindowController.m; sourceTree = "<group>"; };
435B1DFC228B4CB500E853F3 /* ScalarMockAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMockAboutWindowController.h; sourceTree = "<group>"; };
435B1DFD228B590100E853F3 /* ScalarMockProductInfoFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMockProductInfoFetcher.h; sourceTree = "<group>"; };
435B1DFE228B590100E853F3 /* ScalarMockProductInfoFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMockProductInfoFetcher.m; sourceTree = "<group>"; };
435B1E00228B6C3900E853F3 /* ScalarMockNotificationCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMockNotificationCenter.h; sourceTree = "<group>"; };
435B1E01228B6C3900E853F3 /* ScalarMockNotificationCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMockNotificationCenter.m; sourceTree = "<group>"; };
435B1E0D228CC1E000E853F3 /* ScalarProcessRunner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarProcessRunner.h; sourceTree = "<group>"; };
435B1E0E228CC1E000E853F3 /* ScalarProcessRunner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarProcessRunner.m; sourceTree = "<group>"; };
43AF0DF222B01C9700E54D48 /* ScalarNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarNotification.h; sourceTree = "<group>"; };
43AF0DF322B01C9700E54D48 /* ScalarMessageListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMessageListener.h; sourceTree = "<group>"; };
43AF0DF422B01C9700E54D48 /* ScalarNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarNotification.m; sourceTree = "<group>"; };
43AF0DF522B01C9700E54D48 /* ScalarMessageParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMessageParser.m; sourceTree = "<group>"; };
43AF0DF622B01C9800E54D48 /* ScalarMessageListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMessageListener.m; sourceTree = "<group>"; };
43AF0DF722B01C9800E54D48 /* ScalarMessageParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMessageParser.h; sourceTree = "<group>"; };
43AF0E0022B01E3200E54D48 /* ScalarNotificationDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarNotificationDisplay.h; sourceTree = "<group>"; };
43AF0E0122B01E3200E54D48 /* ScalarNotificationDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarNotificationDisplay.m; sourceTree = "<group>"; };
43AF0E0622B023F200E54D48 /* ScalarMessageParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMessageParserTests.m; sourceTree = "<group>"; };
43AF0E0722B023F200E54D48 /* ScalarNotificationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarNotificationTests.m; sourceTree = "<group>"; };
43C021EE22B18BDD00F868F2 /* ScalarNotificationErrors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarNotificationErrors.h; sourceTree = "<group>"; };
43C021EF22B18BDD00F868F2 /* ScalarNotificationErrors.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarNotificationErrors.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
433FE811227B593500488730 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
43583C282285E3E1003357D6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
430ED36D2281E6B200319408 /* UI */ = {
isa = PBXGroup;
children = (
43AF0E0022B01E3200E54D48 /* ScalarNotificationDisplay.h */,
43AF0E0122B01E3200E54D48 /* ScalarNotificationDisplay.m */,
433FE8532280C21000488730 /* ScalarStatusBarItem.h */,
433FE8542280C21000488730 /* ScalarStatusBarItem.m */,
433FE8622281D06100488730 /* ScalarAboutWindowController.h */,
433FE8602281D06000488730 /* ScalarAboutWindowController.m */,
433FE8612281D06100488730 /* ScalarAboutWindowController.xib */,
433FE817227B593500488730 /* ScalarAppDelegate.h */,
433FE818227B593500488730 /* ScalarAppDelegate.m */,
433FE81A227B594300488730 /* Assets.xcassets */,
433FE81C227B594300488730 /* MainMenu.xib */,
);
path = UI;
sourceTree = "<group>";
};
433FE80B227B593500488730 = {
isa = PBXGroup;
children = (
433FE816227B593500488730 /* StatusMenuItem */,
43583C2C2285E3E1003357D6 /* Scalar.UnitTests */,
433FE815227B593500488730 /* Products */,
);
sourceTree = "<group>";
};
433FE815227B593500488730 /* Products */ = {
isa = PBXGroup;
children = (
433FE814227B593500488730 /* Scalar.app */,
43583C2B2285E3E1003357D6 /* Scalar.UnitTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
433FE816227B593500488730 /* StatusMenuItem */ = {
isa = PBXGroup;
children = (
43AF0DFF22B01CD400E54D48 /* IPC */,
435B1E04228C88E300E853F3 /* Utilities */,
430ED36D2281E6B200319408 /* UI */,
433FE81F227B594300488730 /* Info.plist */,
433FE820227B594300488730 /* main.m */,
433FE822227B594300488730 /* Scalar.entitlements */,
);
path = StatusMenuItem;
sourceTree = "<group>";
};
43583C2C2285E3E1003357D6 /* Scalar.UnitTests */ = {
isa = PBXGroup;
children = (
43583C3B228A17D4003357D6 /* Mocks */,
43583C2F2285E3E1003357D6 /* Info.plist */,
43583C372289BE94003357D6 /* ScalarAboutWindowControllerTests.m */,
43AF0E0722B023F200E54D48 /* ScalarNotificationTests.m */,
43AF0E0622B023F200E54D48 /* ScalarMessageParserTests.m */,
43583C352289BDAA003357D6 /* ScalarStatusBarItemTests.m */,
);
path = Scalar.UnitTests;
sourceTree = "<group>";
};
43583C3B228A17D4003357D6 /* Mocks */ = {
isa = PBXGroup;
children = (
435B1DFC228B4CB500E853F3 /* ScalarMockAboutWindowController.h */,
435B1DFA228B4C8400E853F3 /* ScalarMockAboutWindowController.m */,
435B1DFD228B590100E853F3 /* ScalarMockProductInfoFetcher.h */,
435B1DFE228B590100E853F3 /* ScalarMockProductInfoFetcher.m */,
435B1E00228B6C3900E853F3 /* ScalarMockNotificationCenter.h */,
435B1E01228B6C3900E853F3 /* ScalarMockNotificationCenter.m */,
);
path = Mocks;
sourceTree = "<group>";
};
435B1E04228C88E300E853F3 /* Utilities */ = {
isa = PBXGroup;
children = (
433FE8582280E10F00488730 /* ScalarProductInfoFetcher.h */,
433FE8592280E10F00488730 /* ScalarProductInfoFetcher.m */,
435B1E0D228CC1E000E853F3 /* ScalarProcessRunner.h */,
435B1E0E228CC1E000E853F3 /* ScalarProcessRunner.m */,
43C021EE22B18BDD00F868F2 /* ScalarNotificationErrors.h */,
43C021EF22B18BDD00F868F2 /* ScalarNotificationErrors.m */,
);
path = Utilities;
sourceTree = "<group>";
};
43AF0DFF22B01CD400E54D48 /* IPC */ = {
isa = PBXGroup;
children = (
43AF0DF222B01C9700E54D48 /* ScalarNotification.h */,
43AF0DF422B01C9700E54D48 /* ScalarNotification.m */,
43AF0DF322B01C9700E54D48 /* ScalarMessageListener.h */,
43AF0DF622B01C9800E54D48 /* ScalarMessageListener.m */,
43AF0DF722B01C9800E54D48 /* ScalarMessageParser.h */,
43AF0DF522B01C9700E54D48 /* ScalarMessageParser.m */,
);
path = IPC;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
433FE813227B593500488730 /* Scalar */ = {
isa = PBXNativeTarget;
buildConfigurationList = 433FE830227B594300488730 /* Build configuration list for PBXNativeTarget "Scalar" */;
buildPhases = (
433FE810227B593500488730 /* Sources */,
433FE811227B593500488730 /* Frameworks */,
433FE812227B593500488730 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Scalar;
productName = Scalar;
productReference = 433FE814227B593500488730 /* Scalar.app */;
productType = "com.apple.product-type.application";
};
43583C2A2285E3E1003357D6 /* Scalar.UnitTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 43583C322285E3E1003357D6 /* Build configuration list for PBXNativeTarget "Scalar.UnitTests" */;
buildPhases = (
43583C272285E3E1003357D6 /* Sources */,
43583C282285E3E1003357D6 /* Frameworks */,
43583C292285E3E1003357D6 /* Resources */,
);
buildRules = (
);
dependencies = (
43583C312285E3E1003357D6 /* PBXTargetDependency */,
);
name = Scalar.UnitTests;
productName = Scalar.UnitTests;
productReference = 43583C2B2285E3E1003357D6 /* Scalar.UnitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
433FE80C227B593500488730 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1100;
ORGANIZATIONNAME = Microsoft;
TargetAttributes = {
433FE813227B593500488730 = {
CreatedOnToolsVersion = 9.3;
};
43583C2A2285E3E1003357D6 = {
CreatedOnToolsVersion = 9.3;
TestTargetID = 433FE813227B593500488730;
};
};
};
buildConfigurationList = 433FE80F227B593500488730 /* Build configuration list for PBXProject "Scalar" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 433FE80B227B593500488730;
productRefGroup = 433FE815227B593500488730 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
433FE813227B593500488730 /* Scalar */,
43583C2A2285E3E1003357D6 /* Scalar.UnitTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
433FE812227B593500488730 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
433FE8642281D06100488730 /* ScalarAboutWindowController.xib in Resources */,
433FE81B227B594300488730 /* Assets.xcassets in Resources */,
433FE81E227B594300488730 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
43583C292285E3E1003357D6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
433FE810227B593500488730 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
433FE85A2280E10F00488730 /* ScalarProductInfoFetcher.m in Sources */,
435B1E0F228CC1E000E853F3 /* ScalarProcessRunner.m in Sources */,
433FE8632281D06100488730 /* ScalarAboutWindowController.m in Sources */,
43AF0DF822B01C9800E54D48 /* ScalarNotification.m in Sources */,
433FE821227B594300488730 /* main.m in Sources */,
43C021F022B18BDD00F868F2 /* ScalarNotificationErrors.m in Sources */,
43AF0DFA22B01C9800E54D48 /* ScalarMessageParser.m in Sources */,
433FE819227B593500488730 /* ScalarAppDelegate.m in Sources */,
43AF0E0222B01E3200E54D48 /* ScalarNotificationDisplay.m in Sources */,
433FE8562280C21000488730 /* ScalarStatusBarItem.m in Sources */,
43AF0DFC22B01C9800E54D48 /* ScalarMessageListener.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
43583C272285E3E1003357D6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
43AF0E0322B023BE00E54D48 /* ScalarMockAboutWindowController.m in Sources */,
43AF0E0422B023BE00E54D48 /* ScalarMockProductInfoFetcher.m in Sources */,
43AF0E0822B023F200E54D48 /* ScalarMessageParserTests.m in Sources */,
43AF0E0922B023F200E54D48 /* ScalarNotificationTests.m in Sources */,
43AF0E0522B023BE00E54D48 /* ScalarMockNotificationCenter.m in Sources */,
43583C362289BDAA003357D6 /* ScalarStatusBarItemTests.m in Sources */,
43583C382289BE94003357D6 /* ScalarAboutWindowControllerTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
43583C312285E3E1003357D6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 433FE813227B593500488730 /* Scalar */;
targetProxy = 43583C302285E3E1003357D6 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
433FE81C227B594300488730 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
433FE81D227B594300488730 /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
433FE82E227B594300488730 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
433FE82F227B594300488730 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
433FE831227B594300488730 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
CODE_SIGN_ENTITLEMENTS = "";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = UBF8T346G9;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = StatusMenuItem/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.Scalar;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
433FE832227B594300488730 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
CODE_SIGN_ENTITLEMENTS = "";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = UBF8T346G9;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = StatusMenuItem/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.Scalar;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
43583C332285E3E1003357D6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = UBF8T346G9;
HEADER_SEARCH_PATHS = "$(SRCROOT)/Scalar.UnitTests/Mocks";
INFOPLIST_FILE = Scalar.UnitTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.Scalar-UnitTests";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Scalar.app/Contents/MacOS/Scalar";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
43583C342285E3E1003357D6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = UBF8T346G9;
HEADER_SEARCH_PATHS = "$(SRCROOT)/Scalar.UnitTests/Mocks";
INFOPLIST_FILE = Scalar.UnitTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.Scalar-UnitTests";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Scalar.app/Contents/MacOS/Scalar";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
433FE80F227B593500488730 /* Build configuration list for PBXProject "Scalar" */ = {
isa = XCConfigurationList;
buildConfigurations = (
433FE82E227B594300488730 /* Debug */,
433FE82F227B594300488730 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
433FE830227B594300488730 /* Build configuration list for PBXNativeTarget "Scalar" */ = {
isa = XCConfigurationList;
buildConfigurations = (
433FE831227B594300488730 /* Debug */,
433FE832227B594300488730 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
43583C322285E3E1003357D6 /* Build configuration list for PBXNativeTarget "Scalar.UnitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
43583C332285E3E1003357D6 /* Debug */,
43583C342285E3E1003357D6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 433FE80C227B593500488730 /* Project object */;
}

Просмотреть файл

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Scalar.xcodeproj">
</FileRef>
</Workspace>

Просмотреть файл

@ -1,8 +0,0 @@
<?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">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -1,19 +0,0 @@
#import <Foundation/Foundation.h>
typedef void (^NewMessageCallback) (NSDictionary *_Nonnull messageInfo);
NS_ASSUME_NONNULL_BEGIN
@interface ScalarMessageListener : NSObject
@property (copy) NSString *socketPath;
@property (copy) NewMessageCallback messageCallback;
- (instancetype _Nullable)initWithSocket:(NSString *)socketPath
callback:(NewMessageCallback)callback;
- (BOOL)startListening;
- (void)stopListening;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,163 +0,0 @@
#import <netinet/in.h>
#import <sys/socket.h>
#import <sys/un.h>
#import "ScalarMessageListener.h"
#import "ScalarMessageParser.h"
NSString * const NotificationServerPipeName = @"scalar.notification";
@interface ScalarMessageListener ()
@property (strong) NSFileHandle *connectionHandle;
@property CFSocketRef socketRef;
@end
@implementation ScalarMessageListener
- (instancetype)initWithSocket:(NSString *)socketPath
callback:(nonnull NewMessageCallback)callback
{
if (self = [super init])
{
_socketPath =
[[socketPath stringByAppendingPathComponent:NotificationServerPipeName]
copy];
_messageCallback = [callback copy];
}
return self;
}
- (void) dealloc
{
if (_socketRef != NULL)
{
CFSocketInvalidate(_socketRef);
CFRelease(_socketRef);
}
}
- (BOOL)startListening
{
CFSocketRef cfSocket;
if ((cfSocket = CFSocketCreate(kCFAllocatorDefault,
PF_LOCAL,
SOCK_STREAM,
0,
kCFSocketNoCallBack,
NULL,
NULL)) == NULL)
{
return NO;
}
CFAutorelease(cfSocket);
int reuse = TRUE;
int socketDescriptor = CFSocketGetNative(cfSocket);
if (setsockopt(socketDescriptor,
SOL_SOCKET,
SO_REUSEADDR,
(void *) &reuse,
sizeof(reuse)))
{
return NO;
}
if ([[NSFileManager defaultManager] fileExistsAtPath:self.socketPath] &&
![[NSFileManager defaultManager] removeItemAtPath:self.socketPath error:nil])
{
return NO;
}
struct sockaddr_un sockAddress = {};
memset(&sockAddress, 0, sizeof(sockAddress));
sockAddress.sun_family = AF_UNIX;
sockAddress.sun_len = sizeof(sockAddress);
[self.socketPath getCString:sockAddress.sun_path
maxLength:sizeof(sockAddress.sun_path)
encoding:NSUTF8StringEncoding];
CFDataRef addressData = CFDataCreate(kCFAllocatorDefault,
(const UInt8 *) &sockAddress,
sizeof(sockAddress));
CFAutorelease(addressData);
if (CFSocketSetAddress(cfSocket, addressData) != kCFSocketSuccess)
{
return NO;
}
self.socketRef = (CFSocketRef) CFRetain(cfSocket);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(messageReadCompleteCallback:)
name:NSFileHandleReadToEndOfFileCompletionNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(newConnectionCallback:)
name:NSFileHandleConnectionAcceptedNotification
object:nil];
self.connectionHandle = [[NSFileHandle alloc] initWithFileDescriptor:socketDescriptor
closeOnDealloc:YES];
[self.connectionHandle acceptConnectionInBackgroundAndNotify];
return YES;
}
- (void)stopListening
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
CFSocketInvalidate(self.socketRef);
CFRelease(self.socketRef);
self.socketRef = NULL;
self.connectionHandle = nil;
}
- (void)newConnectionCallback:(NSNotification *)notification
{
@autoreleasepool
{
NSFileHandle *readHandle = [[notification userInfo]
objectForKey:NSFileHandleNotificationFileHandleItem];
if (readHandle != nil)
{
[readHandle readToEndOfFileInBackgroundAndNotify];
}
[self.connectionHandle acceptConnectionInBackgroundAndNotify];
}
}
- (void)messageReadCompleteCallback:(NSNotification *)notification
{
@autoreleasepool
{
NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
if ((data != nil) && ([data length] > 0))
{
NSError *error;
NSDictionary *message;
if([ScalarMessageParser tryParseData:data message:&message error:&error])
{
self.messageCallback(message);
}
else
{
NSLog(@"ERROR: Could not parse notification payload: %@.", [error description]);
}
}
else
{
NSNumber *unixError = [[notification userInfo] objectForKey:@"NSFileHandleError"];
NSLog(@"ERROR: Could not read data from socket %s.",
unixError != nil ? strerror([unixError intValue]) : "");
}
}
}
@end

Просмотреть файл

@ -1,13 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ScalarMessageParser : NSObject
+ (BOOL)tryParseData:(NSData *)data
message:(NSDictionary *_Nullable __autoreleasing *_Nonnull)parsedMessage
error:(NSError *__autoreleasing *)error;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,63 +0,0 @@
#import "ScalarMessageParser.h"
#import "ScalarNotificationErrors.h"
NSString * const NotificationPrefix = @"Notification|";
@implementation ScalarMessageParser
+ (BOOL)tryParseData:(NSData *)data
message:(NSDictionary *__autoreleasing *)parsedMessage
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(parsedMessage);
NSString *messageStr;
if (!(messageStr = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]))
{
if (error != nil)
{
NSString *info = [NSString stringWithFormat:@"%@: ERROR: error reading data.",
NSStringFromSelector(_cmd)];
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:ScalarMessageReadError
userInfo:@{ NSLocalizedDescriptionKey : info }];
}
*parsedMessage = nil;
return NO;
}
if ([messageStr hasPrefix:NotificationPrefix])
{
messageStr = [messageStr substringFromIndex:[NotificationPrefix length]];
}
messageStr = [messageStr stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
NSError *parseError;
if (!(*parsedMessage = [NSJSONSerialization
JSONObjectWithData:[messageStr dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:&parseError]))
{
if (error != nil)
{
if (parseError == nil)
{
NSString *info = [NSString stringWithFormat:@"%@: ERROR: Unknown parse error.",
NSStringFromSelector(_cmd)];
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:ScalarMessageParseError
userInfo:@{ NSLocalizedDescriptionKey : info }];
}
else
{
*error = parseError;
}
}
}
return *parsedMessage != nil;
}
@end

Просмотреть файл

@ -1,26 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString * const KnownMessagePrefix;
typedef NS_ENUM(NSInteger, Identifier)
{
AutomountStart,
MountSuccess,
MountFailure,
UnknownMessage
};
@interface ScalarNotification : NSObject
@property (assign, readonly) Identifier identifier;
@property (copy, readonly) NSString *title;
@property (copy, readonly) NSString *message;
+ (BOOL)tryValidateMessage:(NSDictionary *)jsonMessage
buildNotification:(ScalarNotification *_Nullable *_Nonnull)notification
error:(NSError *__autoreleasing *)error;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,184 +0,0 @@
#import "ScalarNotification.h"
#import "ScalarNotificationErrors.h"
NSString * const IdentifierKey = @"Id";
NSString * const EnlistmentKey = @"Enlistment";
NSString * const EnlistmentCountKey = @"EnlistmentCount";
NSString * const TitleKey = @"Title";
NSString * const MessageKey = @"Message";
NSString * const AutomountTitle = @"Scalar AutoMount";
NSString * const AutomountStartMessageFormat = @"Attempting to mount %lu Scalar repos(s)";
NSString * const AutomountSuccessMessageFormat = @"The following Scalar repo is now mounted: \n%@";
NSString * const AutomountFailureMessageFormat = @"The following Scalar repo failed to mount: \n%@";
@interface ScalarNotification()
@property (readwrite) NSString *title;
@property (readwrite) NSString *message;
NS_ASSUME_NONNULL_BEGIN
- (instancetype _Nullable)initAsMountSuccessWithMessage:(NSDictionary *)messageDict
error:(NSError *__autoreleasing *)error;
- (instancetype _Nullable)initAsMountFailureWithMessage:(NSDictionary *)messageDict
error:(NSError *__autoreleasing *)error;
- (instancetype _Nullable)initAsMountWithMessage:(NSDictionary *)messageDict
title:(NSString *)title
messageFormat:(NSString *)messageFormat
error:(NSError *__autoreleasing *)error;
NS_ASSUME_NONNULL_END
@end
@implementation ScalarNotification
+ (BOOL)tryValidateMessage:(NSDictionary *)jsonMessage
buildNotification:(ScalarNotification **)notification
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(notification);
NSParameterAssert(jsonMessage);
id identifier = jsonMessage[IdentifierKey];
if (![identifier isKindOfClass:[NSNumber class]])
{
if (error != nil)
{
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:ScalarInvalidMessageIdFormatError
userInfo:@{ NSLocalizedDescriptionKey : @"Unexpected message id/format)" }];
}
return NO;
}
Identifier idValue = [identifier integerValue];
NSError *initError = nil;
switch (idValue)
{
case AutomountStart:
{
*notification = [[ScalarNotification alloc]
initAsAutomountStartWithMessage:jsonMessage
error:&initError];
break;
}
case MountSuccess:
{
*notification = [[ScalarNotification alloc]
initAsMountSuccessWithMessage:jsonMessage
error:&initError];
break;
}
case MountFailure:
{
*notification = [[ScalarNotification alloc]
initAsMountFailureWithMessage:jsonMessage
error:&initError];
break;
}
default:
{
*notification = nil;
initError = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:ScalarUnsupportedMessageError
userInfo:@{ NSLocalizedDescriptionKey : @"Unrecognised message id" }];
break;
}
}
if (error != nil)
{
*error = initError;
}
return *notification != nil;
}
#pragma mark Private initializers
- (instancetype)initAsAutomountStartWithMessage:(NSDictionary *)messageDict
error:(NSError *__autoreleasing *)error
{
if (self = [super init])
{
id repoCount = messageDict[EnlistmentCountKey];
if (repoCount && [repoCount isKindOfClass:[NSNumber class]])
{
_title = [AutomountTitle copy];
_message = [[NSString stringWithFormat:AutomountStartMessageFormat, [repoCount unsignedIntegerValue]] copy];
return self;
}
if (error != nil)
{
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:ScalarMissingRepoCountError
userInfo:@{ NSLocalizedDescriptionKey : @"Missing repos count in AutomountStart message" }];
}
self = nil;
}
return self;
}
- (instancetype)initAsMountSuccessWithMessage:(NSDictionary *)messageDict
error:(NSError *__autoreleasing *)error
{
return self = [self initAsMountWithMessage:messageDict
title:(NSString *)AutomountTitle
messageFormat:(NSString *)AutomountSuccessMessageFormat
error:error];
}
- (instancetype)initAsMountFailureWithMessage:(NSDictionary *)messageDict
error:(NSError *__autoreleasing *)error
{
return self = [self initAsMountWithMessage:messageDict
title:(NSString *)AutomountTitle
messageFormat:(NSString *)AutomountFailureMessageFormat
error:error];
}
- (instancetype)initAsMountWithMessage:(NSDictionary *)messageDict
title:(NSString *)title
messageFormat:(NSString *)messageFormat
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(title);
NSParameterAssert(messageFormat);
if (self = [super init])
{
id enlistment = messageDict[EnlistmentKey];
if (enlistment && [enlistment isKindOfClass:[NSString class]])
{
_title = [title copy];
_message = [[NSString stringWithFormat:
(NSString *)messageFormat,
enlistment] copy];
return self;
}
if (error != nil)
{
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:ScalarMissingEntitlementInfoError
userInfo:@{ NSLocalizedDescriptionKey : @"ERROR: missing enlistment info." }];
}
self = nil;
}
return self;
}
@end

Просмотреть файл

@ -1,36 +0,0 @@
<?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">
<dict>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSUIElement</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Microsoft. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

Просмотреть файл

@ -1,10 +0,0 @@
<?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">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Просмотреть файл

@ -1,68 +0,0 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Scalar_16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Scalar_32-1.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "Scalar_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "Scalar_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "Scalar_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "Scalar_256-1.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "Scalar_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "Scalar_512-1.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "Scalar_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "Scalar_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 41 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 4.3 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 590 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 8.8 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 8.8 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 19 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 19 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Просмотреть файл

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "Scalar_16.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Scalar_32.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Scalar_64.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 590 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Просмотреть файл

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="ScalarAppDelegate">
<connections>
<outlet property="Window" destination="QvC-M9-y7g" id="ykX-6N-Ma5"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Scalar" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Scalar" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Scalar" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Quit Scalar" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="-66" y="25"/>
</menu>
<window title="Scalar" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<point key="canvasLocation" x="63" y="363"/>
</window>
</objects>
</document>

Просмотреть файл

@ -1,11 +0,0 @@
#import <Cocoa/Cocoa.h>
#import "ScalarProductInfoFetcher.h"
@interface ScalarAboutWindowController : NSWindowController
@property (readonly, nullable) NSString *scalarVersion;
@property (readonly, nullable) NSString *gitVersion;
- (instancetype _Nullable)initWithProductInfoFetcher:(ScalarProductInfoFetcher *_Nonnull)productInfoFetcher;
@end

Просмотреть файл

@ -1,55 +0,0 @@
#import "ScalarAboutWindowController.h"
@interface ScalarAboutWindowController ()
@property (strong) ScalarProductInfoFetcher *productInfoFetcher;
@end
@implementation ScalarAboutWindowController
- (instancetype)initWithProductInfoFetcher:(ScalarProductInfoFetcher *)productInfoFetcher
{
if (productInfoFetcher == nil)
{
self = nil;
}
else if (self = [super initWithWindowNibName:@"ScalarAboutWindowController"])
{
_productInfoFetcher = productInfoFetcher;
}
return self;
}
- (NSString *)scalarVersion
{
NSString *version;
NSError *error;
if ([self.productInfoFetcher tryGetScalarVersion:&version error:&error])
{
return version;
}
else
{
NSLog(@"Error getting Scalar version: %@", [error description]);
return @"Not available";
}
}
- (NSString *)gitVersion
{
NSString *version;
NSError *error;
if ([self.productInfoFetcher tryGetGitVersion:&version error:&error])
{
return version;
}
else
{
NSLog(@"Error getting Git version: %@", [error description]);
return @"Not available";
}
}
@end

Просмотреть файл

@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
<capability name="box content view" minToolsVersion="7.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
<capability name="system font weights other than Regular or Bold" minToolsVersion="7.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ScalarAboutWindowController">
<connections>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="About Scalar" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="utilityWindow" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" canJoinAllSpaces="YES" ignoresCycle="YES" fullScreenAuxiliary="YES"/>
<rect key="contentRect" x="784" y="333" width="520" height="191"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
<value key="minSize" type="size" width="520" height="191"/>
<value key="maxSize" type="size" width="520" height="191"/>
<value key="minFullScreenContentSize" type="size" width="520" height="191"/>
<value key="maxFullScreenContentSize" type="size" width="520" height="191"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="520" height="191"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<box autoresizesSubviews="NO" fixedFrame="YES" borderType="line" title="Version Info..." translatesAutoresizingMaskIntoConstraints="NO" id="bl4-5a-Z0p">
<rect key="frame" x="176" y="16" width="327" height="155"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="ZS0-xZ-RRj">
<rect key="frame" x="1" y="1" width="325" height="139"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rzD-cd-GNF">
<rect key="frame" x="18" y="19" width="288" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Scalar Version" id="hMd-va-lMt">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="gitVersion" id="cD3-Y6-vdy"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pGO-kl-sTg">
<rect key="frame" x="18" y="101" width="75" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Scalar" allowsEditingTextAttributes="YES" id="zLl-4G-TZk">
<font key="font" metaFont="systemSemibold" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kj1-ex-ImL">
<rect key="frame" x="18" y="39" width="23" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Git" id="xeY-Ah-BYF">
<font key="font" metaFont="systemSemibold" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2NU-O7-nN1">
<rect key="frame" x="18" y="81" width="288" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Scalar Version" id="sFb-nd-mdX">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="scalarVersion" id="6Wp-ud-281"/>
</connections>
</textField>
</subviews>
</view>
</box>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UtS-Mf-ucW">
<rect key="frame" x="12" y="19" width="154" height="154"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSApplicationIcon" id="dl0-H5-uoz"/>
</imageView>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="98" y="100.5"/>
</window>
</objects>
<resources>
<image name="NSApplicationIcon" width="128" height="128"/>
</resources>
</document>

Просмотреть файл

@ -1,6 +0,0 @@
#import <Cocoa/Cocoa.h>
@interface ScalarAppDelegate : NSObject <NSApplicationDelegate>
@end

Просмотреть файл

@ -1,72 +0,0 @@
#import "ScalarAboutWindowController.h"
#import "ScalarAppDelegate.h"
#import "ScalarMessageListener.h"
#import "ScalarNotificationDisplay.h"
#import "ScalarNotification.h"
#import "ScalarProductInfoFetcher.h"
#import "ScalarStatusBarItem.h"
#import "ScalarNotificationDisplay.h"
@interface ScalarAppDelegate ()
@property (weak) IBOutlet NSWindow *Window;
@property (strong) ScalarStatusBarItem *StatusDisplay;
@property (strong) ScalarMessageListener *messageListener;
- (void)displayNotification:(NSDictionary *_Nonnull)messageInfo;
@end
@implementation ScalarAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.messageListener = [[ScalarMessageListener alloc]
initWithSocket:NSTemporaryDirectory()
callback:^(NSDictionary *messageInfo)
{
[self displayNotification:messageInfo];
}];
[self.messageListener startListening];
ScalarProductInfoFetcher *productInfoFetcher =
[[ScalarProductInfoFetcher alloc]
initWithProcessRunner:[[ScalarProcessRunner alloc] initWithProcessFactory:^NSTask *
{
return [[NSTask alloc] init];
}]];
self.StatusDisplay = [[ScalarStatusBarItem alloc] initWithAboutWindowController:
[[ScalarAboutWindowController alloc]
initWithProductInfoFetcher:productInfoFetcher]];
[self.StatusDisplay load];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
[self.messageListener stopListening];
}
- (void)displayNotification:(NSDictionary *_Nonnull)messageInfo
{
NSParameterAssert(messageInfo);
ScalarNotification *notification;
NSError *error;
if (![ScalarNotification tryValidateMessage:messageInfo
buildNotification:&notification
error:&error])
{
NSLog(@"ERROR: Could not display notification. %@", [error description]);
return;
}
ScalarNotificationDisplay *notificationDisplay =
[[ScalarNotificationDisplay alloc] initWithTitle:notification.title
message:notification.message];
[notificationDisplay display];
}
@end

Просмотреть файл

@ -1,10 +0,0 @@
#import <Foundation/Foundation.h>
@interface ScalarNotificationDisplay : NSObject
- (instancetype _Nullable)initWithTitle:(NSString *)title message:(NSString *)message;
- (instancetype _Nullable)initWithUserNotification:(NSUserNotification *)userNotification
notificationCenter:(NSUserNotificationCenter *)notificationCenter;
- (void) display;
@end

Просмотреть файл

@ -1,42 +0,0 @@
#import <Cocoa/Cocoa.h>
#import "ScalarNotificationDisplay.h"
@interface ScalarNotificationDisplay ()
@property (strong) NSUserNotification *userNotification;
@property (strong) NSUserNotificationCenter *userNotificationCenter;
@end
@implementation ScalarNotificationDisplay
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message
{
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
userNotification.title = title;
userNotification.informativeText = message;
NSUserNotificationCenter *notificationCenter =
[NSUserNotificationCenter defaultUserNotificationCenter];
return self = [self initWithUserNotification:userNotification
notificationCenter:notificationCenter];
}
- (instancetype)initWithUserNotification:(NSUserNotification *)userNotification
notificationCenter:(NSUserNotificationCenter *)notificationCenter
{
if (self = [super init])
{
_userNotification = userNotification;
_userNotificationCenter = notificationCenter;
}
return self;
}
- (void)display
{
[self.userNotificationCenter deliverNotification:self.userNotification];
}
@end

Просмотреть файл

@ -1,10 +0,0 @@
#import <Cocoa/Cocoa.h>
@interface ScalarStatusBarItem : NSObject
- (instancetype _Nullable)initWithAboutWindowController:(ScalarAboutWindowController *_Nonnull)aboutWindowController;
- (void)load;
- (NSMenu *_Nullable)getStatusMenu;
- (IBAction)handleMenuClick:(id)sender;
@end

Просмотреть файл

@ -1,97 +0,0 @@
#import "ScalarAboutWindowController.h"
#import "ScalarStatusBarItem.h"
#import "ScalarProductInfoFetcher.h"
@interface ScalarStatusBarItem ()
@property (strong, nonnull) NSStatusItem *statusItem;
@property (strong, nonnull) ScalarAboutWindowController *aboutWindowController;
@end
@implementation ScalarStatusBarItem
- (instancetype)initWithAboutWindowController:(ScalarAboutWindowController *)aboutWindowController
{
if (aboutWindowController == nil)
{
self = nil;
}
else if (self = [super init])
{
_aboutWindowController = aboutWindowController;
}
return self;
}
- (void)load
{
self.statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem setHighlightMode:YES];
[self addStatusButton];
[self addMenuItems];
}
- (IBAction)handleMenuClick:(id)sender
{
switch (((NSButton *) sender).tag)
{
case 0:
{
[self displayAboutBox];
break;
}
default:
{
break;
}
}
}
- (IBAction)displayAboutBox
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[self.aboutWindowController showWindow:self];
[self.aboutWindowController.window makeKeyAndOrderFront:self];
}
- (NSMenu *)getStatusMenu
{
return self.statusItem.menu;
}
- (void)addStatusButton
{
NSImage *image = [NSImage imageNamed:@"StatusItem"];
[image setTemplate:YES];
[self.statusItem.button setImage:image];
[self.statusItem.button setTarget:nil];
[self.statusItem.button setAction:nil];
}
- (void)addMenuItems
{
NSUInteger index = 0;
NSMenu *menu = [[NSMenu alloc] init];
NSMenuItem *aboutItem = [[NSMenuItem alloc]
initWithTitle:@"About Scalar"
action:@selector(handleMenuClick:)
keyEquivalent:@""];
[aboutItem setTag:0];
[aboutItem setTarget:self];
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
[menu insertItem:aboutItem atIndex:index++];
[menu setAutoenablesItems:NO];
[self.statusItem setMenu:menu];
}
@end

Просмотреть файл

@ -1,18 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
extern NSErrorDomain const ScalarNotificationErrorDomain;
typedef NS_ERROR_ENUM(ScalarNotificationErrorDomain, ScalarNotificationErrorCode)
{
ScalarInitError = 200,
ScalarAllocError,
ScalarInvalidMessageIdFormatError,
ScalarUnsupportedMessageError,
ScalarMissingEntitlementInfoError,
ScalarMissingRepoCountError,
ScalarMessageParseError,
ScalarMessageReadError,
};
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,3 +0,0 @@
#import "ScalarNotificationErrors.h"
NSErrorDomain const ScalarNotificationErrorDomain = @"ScalarNotificationErrorDomain";

Просмотреть файл

@ -1,17 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NSTask *_Nonnull (^ProcessFactory)(void);
@interface ScalarProcessRunner : NSObject
- (instancetype _Nullable)initWithProcessFactory:(ProcessFactory)processFactory;
- (BOOL)tryRunExecutable:(NSURL *)path
args:(NSArray<NSString *> *_Nullable)args
output:(NSString *_Nullable __autoreleasing *_Nonnull)output
error:(NSError * __autoreleasing *)error;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,82 +0,0 @@
#import "ScalarProcessRunner.h"
#import "ScalarNotificationErrors.h"
@interface ScalarProcessRunner()
@property (strong) ProcessFactory processFactory;
@end
@implementation ScalarProcessRunner
- (instancetype)initWithProcessFactory:(ProcessFactory)processFactory
{
if (processFactory == nil)
{
self = nil;
}
else if (self = [super init])
{
_processFactory = processFactory;
}
return self;
}
/**
Runs an executable specified by path and args. If the executable could be run
successfully - output will contain the executable's combined stderr/stdout
output and the method returns YES. In case of failure, it returns NO and error
will hold the executable's combined stderr/stdout output.
@param path - specify full path to the executable.
@param args - specify any command line args to pass to the executable.
@param output - contains executable's output, if it was successfully run.
@param error - contains executable's output, if it exited with an error.
@return YES if the executable was successfully run, NO otherwise.
*/
- (BOOL)tryRunExecutable:(NSURL *)path
args:(NSArray<NSString *> *)args
output:(NSString *__autoreleasing *)output
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(path);
NSParameterAssert(output);
NSTask *task = self.processFactory();
NSPipe *taskOut = [NSPipe pipe];
task.executableURL = path;
task.arguments = args;
task.standardOutput = taskOut;
task.standardError = taskOut;
int exitCode = -1;
if ([task launchAndReturnError:error])
{
[task waitUntilExit];
exitCode = [task terminationStatus];
*output = [[NSString alloc] initWithData:[taskOut.fileHandleForReading availableData]
encoding:NSUTF8StringEncoding];
if (0 != exitCode)
{
if (error != nil)
{
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : *output };
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
code:exitCode
userInfo:userInfo];
}
*output = nil;
}
}
return 0 == exitCode;
}
@end

Просмотреть файл

@ -1,16 +0,0 @@
#import <Foundation/Foundation.h>
#import "ScalarProcessRunner.h"
NS_ASSUME_NONNULL_BEGIN
@interface ScalarProductInfoFetcher : NSObject
- (instancetype _Nullable)initWithProcessRunner:(ScalarProcessRunner *)processRunner;
- (BOOL)tryGetGitVersion:(NSString *_Nullable __autoreleasing *_Nonnull)version
error:(NSError **)error;
- (BOOL)tryGetScalarVersion:(NSString *_Nullable __autoreleasing *_Nonnull)version
error:(NSError **)error;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,61 +0,0 @@
#import <objc/runtime.h>
#import "ScalarProductInfoFetcher.h"
NSString * const ScalarPath = @"/usr/local/bin/scalar";
NSString * const GitPath = @"/usr/local/bin/git";
@interface ScalarProductInfoFetcher()
@property (strong, nonnull) ScalarProcessRunner *processRunner;
@end
@implementation ScalarProductInfoFetcher
- (instancetype)initWithProcessRunner:(ScalarProcessRunner *)processRunner
{
if (processRunner == nil)
{
self = nil;
}
else if (self = [super init])
{
_processRunner = processRunner;
}
return self;
}
- (BOOL)tryGetScalarVersion:(NSString *__autoreleasing *)version
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(version);
if (![self.processRunner tryRunExecutable:[NSURL fileURLWithPath:ScalarPath]
args:@[ @"version" ]
output:version
error:error])
{
return NO;
}
return YES;
}
- (BOOL)tryGetGitVersion:(NSString *__autoreleasing *)version
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(version);
if (![self.processRunner tryRunExecutable:[NSURL fileURLWithPath:GitPath]
args:@[ @"version" ]
output:version
error:error])
{
return NO;
}
return YES;
}
@end

Просмотреть файл

@ -1,6 +0,0 @@
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[])
{
return NSApplicationMain(argc, argv);
}

Просмотреть файл

@ -1,41 +0,0 @@
#!/bin/bash
CONFIGURATION=$1
if [ -z $CONFIGURATION ]; then
echo "Error: Build configuration not specified"
exit 1
fi
INT_DIR=$2
if [ -z "${INT_DIR}" ]; then
echo "Error: Intermediate output directory not specified"
exit 1
fi
OUT_DIR=$3
if [ -z "${OUT_DIR}" ]; then
echo "Error: Output directory not specified"
exit 1
fi
VERSION=$4
if [ -z "${VERSION}" ]; then
echo "Info: Version not set; not updating version number"
fi
THIS_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
# Set the version if not the default 'developer version'
if [ -n "$VERSION" ] && [ "$VERSION" != "0.2.173.2" ]; then
updateAppVersionCmd="(cd \"${THIS_DIR}\" && /usr/bin/xcrun agvtool new-marketing-version \"$VERSION\")"
eval $updateAppVersionCmd || exit 1
fi
# Build the product
xcodebuild -configuration "$CONFIGURATION" -project "${THIS_DIR}/Scalar.xcodeproj" build -scheme "Scalar" -derivedDataPath "$INT_DIR" || exit 1
# Ensure the output directory exists
mkdir -p "$OUT_DIR" || exit 1
# Copy output from intermediate output to final binary output directory
cp -Rf "$INT_DIR/Build/Products/$CONFIGURATION/Scalar.app" "$OUT_DIR" || exit 1

Просмотреть файл

@ -1,38 +0,0 @@
<?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">
<dict>
<key>EnvironmentVariables</key>
<dict>
<!--
Although Scalar.app gets launched in the GUI context of actively logged-in user, it
does not seem to inherit any user-specific environment variables (like user customized
$PATH specified in shell ~/.profile). Scalar needs /usr/local/bin to be in its
search PATH environment, so specifying it here, along with rest of the default paths
that are set by launchd.
-->
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>Label</key>
<string>org.scalar.usernotification</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Application Support/Scalar/Scalar.app/Contents/MacOS/Scalar</string>
</array>
<key>WorkingDirectory</key>
<string>/usr/local/scalar</string>
<key>ProcessType</key>
<string>Interactive</string>
<key>LimitLoadToSessionType</key>
<string>Aqua</string>
<key>Disabled</key>
<false/>
<key>OnDemand</key>
<false/>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -1,16 +0,0 @@
#!/bin/bash
CONFIGURATION=$1
if [ -z $CONFIGURATION ]; then
echo "Error: Build configuration not specified"
exit 1
fi
INT_DIR=$2
if [ -z $INT_DIR ]; then
echo "Error: Intermediate output directory not specified"
exit 1
fi
# Run tests
xcodebuild -configuration "$CONFIGURATION" -project "Scalar.xcodeproj" test -scheme "Scalar" -derivedDataPath "$INT_DIR" || exit 1

Просмотреть файл

@ -1,45 +0,0 @@
using Scalar.Common.NamedPipes;
using Scalar.Common.Tracing;
using System;
using System.IO;
namespace Scalar.Service.Handlers
{
public class MacNotificationHandler : INotificationHandler
{
private const string NotificationServerPipeName = "scalar.notification";
private ITracer tracer;
public MacNotificationHandler(ITracer tracer)
{
this.tracer = tracer;
}
public void SendNotification(NamedPipeMessages.Notification.Request request)
{
string pipeName = Path.Combine(Path.GetTempPath(), NotificationServerPipeName);
using (NamedPipeClient client = new NamedPipeClient(pipeName))
{
if (client.Connect())
{
try
{
client.SendRequest(request.ToMessage());
}
catch (Exception ex)
{
EventMetadata metadata = new EventMetadata();
metadata.Add("Area", "NotificationHandler");
metadata.Add("Exception", ex.ToString());
metadata.Add(TracingConstants.MessageKey.ErrorMessage, "MacOS notification display error");
this.tracer.RelatedError(metadata, $"MacOS notification: {request.Title} - {request.Message}.");
}
}
else
{
this.tracer.RelatedError($"ERROR: Communication failure with native notification display tool. Notification info: {request.Title} - {request.Message}.");
}
}
}
}
}

Просмотреть файл

@ -1,38 +0,0 @@
<?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">
<dict>
<key>EnvironmentVariables</key>
<dict>
<!--
Although Scalar.Service gets launched in the GUI context of actively logged-in user, it
does not seem to inherit any user-specific environment variables (like user customized
$PATH specified in shell ~/.profile). Scalar needs /usr/local/bin to be in its
search PATH environment, so specifying it here, along with rest of the default paths
that are set by launchd.
-->
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>Label</key>
<string>org.scalar.service</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/scalar/Scalar.Service</string>
</array>
<key>WorkingDirectory</key>
<string>/usr/local/scalar</string>
<key>ProcessType</key>
<string>Interactive</string>
<key>LimitLoadToSessionType</key>
<string>Aqua</string>
<key>Disabled</key>
<false/>
<key>OnDemand</key>
<false/>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -1,142 +0,0 @@
using Scalar.Common;
using Scalar.Common.FileSystem;
using Scalar.Common.NamedPipes;
using Scalar.Common.RepoRegistry;
using Scalar.Common.Tracing;
using Scalar.Service.Handlers;
using System;
using System.Threading;
namespace Scalar.Service
{
public class MacScalarService
{
public const string ServiceNameArgPrefix = "--servicename=";
private const string EtwArea = "ScalarService";
private ITracer tracer;
private Thread serviceThread;
private ManualResetEvent serviceStopped;
private string serviceName;
private IScalarRepoRegistry repoRegistry;
private RequestHandler requestHandler;
private MaintenanceTaskScheduler maintenanceTaskScheduler;
public MacScalarService(
ITracer tracer,
string serviceName,
IScalarRepoRegistry repoRegistry)
{
this.tracer = tracer;
this.repoRegistry = repoRegistry;
this.serviceName = serviceName;
this.serviceStopped = new ManualResetEvent(false);
this.serviceThread = new Thread(this.ServiceThreadMain);
this.requestHandler = new RequestHandler(this.tracer, EtwArea);
}
public void Run()
{
try
{
if (!string.IsNullOrEmpty(this.serviceName))
{
string pipeName = ScalarPlatform.Instance.GetScalarServiceNamedPipeName(this.serviceName);
this.tracer.RelatedInfo("Starting pipe server with name: " + pipeName);
using (NamedPipeServer pipeServer = NamedPipeServer.StartNewServer(
pipeName,
this.tracer,
this.requestHandler.HandleRequest))
{
this.serviceThread.Start();
this.serviceThread.Join();
}
}
else
{
this.tracer.RelatedError("No name specified for Service Pipe.");
}
}
catch (Exception e)
{
this.LogExceptionAndExit(e, nameof(this.Run));
}
}
private static EventMetadata CreateEventMetadata(Exception e = null)
{
EventMetadata metadata = new EventMetadata();
metadata.Add("Area", EtwArea);
if (e != null)
{
metadata.Add("Exception", e.ToString());
}
return metadata;
}
private void ServiceThreadMain()
{
try
{
string currentUser = ScalarPlatform.Instance.GetCurrentUser();
EventMetadata metadata = new EventMetadata();
metadata.Add("Version", ProcessHelper.GetCurrentProcessVersion());
metadata.Add(nameof(currentUser), currentUser);
this.tracer.RelatedEvent(EventLevel.Informational, $"ScalarService_{nameof(this.ServiceThreadMain)}", metadata);
if (int.TryParse(currentUser, out int sessionId))
{
try
{
this.maintenanceTaskScheduler = new MaintenanceTaskScheduler(
this.tracer,
new PhysicalFileSystem(),
new MacScalarVerbRunner(this.tracer),
this.repoRegistry);
// On Mac, there is no separate session Id. currentUser is used as sessionId
this.maintenanceTaskScheduler.RegisterUser(new UserAndSession(currentUser, sessionId));
this.maintenanceTaskScheduler.ScheduleRecurringTasks();
}
catch (Exception e)
{
this.tracer.RelatedError(CreateEventMetadata(e), "Failed to start maintenance scheduler");
}
}
else
{
EventMetadata errorMetadata = CreateEventMetadata();
errorMetadata.Add(nameof(currentUser), currentUser);
this.tracer.RelatedError(
errorMetadata,
$"{nameof(this.ServiceThreadMain)}: Failed to parse current user as int.");
}
this.serviceStopped.WaitOne();
this.serviceStopped.Dispose();
this.serviceStopped = null;
if (this.maintenanceTaskScheduler != null)
{
this.maintenanceTaskScheduler.Dispose();
this.maintenanceTaskScheduler = null;
}
}
catch (Exception e)
{
this.LogExceptionAndExit(e, nameof(this.ServiceThreadMain));
}
}
private void LogExceptionAndExit(Exception e, string method)
{
this.tracer.RelatedError(CreateEventMetadata(e), "Unhandled exception in " + method);
Environment.Exit((int)ReturnCode.GenericError);
}
}
}

Просмотреть файл

@ -1,101 +0,0 @@
using Scalar.Common;
using Scalar.Common.Maintenance;
using Scalar.Common.Tracing;
using System.Diagnostics;
using System.IO;
namespace Scalar.Service
{
public class MacScalarVerbRunner : IScalarVerbRunner
{
private readonly string scalarBinPath;
private readonly string internalVerbJson;
private ScalarProcessLauncher processLauncher;
private ITracer tracer;
public MacScalarVerbRunner(ITracer tracer, ScalarProcessLauncher processLauncher = null)
{
this.tracer = tracer;
this.processLauncher = processLauncher ?? new ScalarProcessLauncher(tracer);
this.scalarBinPath = Path.Combine(
ScalarPlatform.Instance.Constants.ScalarBinDirectoryPath,
ScalarPlatform.Instance.Constants.ScalarExecutableName);
InternalVerbParameters internalParams = new InternalVerbParameters(startedByService: true);
this.internalVerbJson = internalParams.ToJson();
}
/// <summary>
/// Calls the 'scalar maintenance' verb
/// </summary>
/// <param name="task">Maintenance task to run</param>
/// <param name="repoRoot">Repo to maintain</param>
/// <param name="sessionId">Ignored</param>
/// <returns>
/// true if the maintenance verb succeeded, and false otherwise
/// </returns>
/// <remarks>
/// 'CallMaintenance' should only be called for repos that are owned by
/// the owner of the current process.
///
/// 'launchctl asuser' *could* be used to launch has an arbitrary user,
/// however, it is not used because it does not pass back the output/errors
/// of the maintenance verb correctly.
///
/// On Mac this method:
///
/// - Is only called by Scalar.Service
/// - Is only called for repos owned by the same user that's running Scalar.Service
///
/// And so there is no need to use 'launchctl'.
/// </remarks>
public bool CallMaintenance(MaintenanceTasks.Task task, string repoRoot, int sessionId)
{
string taskVerbName = MaintenanceTasks.GetVerbTaskName(task);
string arguments =
$"run {taskVerbName} \"{repoRoot}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {this.internalVerbJson}";
ProcessResult result = this.processLauncher.LaunchProcess(this.scalarBinPath, arguments, repoRoot);
if (result.ExitCode != 0)
{
EventMetadata metadata = new EventMetadata();
metadata.Add("Area", "ScalarVerbRunner");
metadata.Add(nameof(this.scalarBinPath), this.scalarBinPath);
metadata.Add(nameof(arguments), arguments);
metadata.Add(nameof(repoRoot), repoRoot);
metadata.Add(nameof(result.ExitCode), result.ExitCode);
metadata.Add(nameof(result.Output), result.Output);
metadata.Add(nameof(result.Errors), result.Errors);
this.tracer.RelatedError(metadata, $"{nameof(this.CallMaintenance)}: Maintenance verb failed");
return false;
}
return true;
}
public class ScalarProcessLauncher
{
private ITracer tracer;
public ScalarProcessLauncher(ITracer tracer)
{
this.tracer = tracer;
}
public virtual ProcessResult LaunchProcess(string executablePath, string arguments, string workingDirectory)
{
ProcessStartInfo processInfo = new ProcessStartInfo(executablePath);
processInfo.Arguments = arguments;
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.WorkingDirectory = workingDirectory;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
return ProcessHelper.Run(processInfo);
}
}
}
}

Просмотреть файл

@ -32,12 +32,6 @@ namespace Scalar.Service
ServiceBase.Run(service);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
AppDomain.CurrentDomain.UnhandledException += JsonUnhandledExceptionHandler;
CreateMacService(tracer, args).Run();
}
else
{
throw new NotImplementedException();
@ -45,37 +39,6 @@ namespace Scalar.Service
}
}
private static MacScalarService CreateMacService(JsonTracer tracer, string[] args)
{
string serviceName = args.FirstOrDefault(arg => arg.StartsWith(MacScalarService.ServiceNameArgPrefix, StringComparison.OrdinalIgnoreCase));
if (serviceName != null)
{
serviceName = serviceName.Substring(MacScalarService.ServiceNameArgPrefix.Length);
}
else
{
serviceName = ScalarConstants.Service.ServiceName;
}
ScalarPlatform scalarPlatform = ScalarPlatform.Instance;
string logFilePath = ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(serviceName);
Directory.CreateDirectory(logFilePath);
tracer.AddLogFileEventListener(
ScalarEnlistment.GetNewScalarLogFileName(logFilePath, ScalarConstants.LogFileTypes.Service),
EventLevel.Informational,
Keywords.Any);
string repoRegistryLocation = scalarPlatform.GetCommonAppDataRootForScalarComponent(ScalarConstants.RepoRegistry.RegistryDirectoryName);
ScalarRepoRegistry repoRegistry = new ScalarRepoRegistry(
tracer,
new PhysicalFileSystem(),
repoRegistryLocation);
return new MacScalarService(tracer, serviceName, repoRegistry);
}
private static void JsonUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
using (JsonTracer tracer = new JsonTracer(ScalarConstants.Service.ServiceName, ScalarConstants.Service.ServiceName))

Просмотреть файл

@ -14,7 +14,4 @@ echo Copying signed files...
xcopy "%SIGNDIR%\pe\scalar.dll" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
xcopy "%SIGNDIR%\pe\scalar.common.dll" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
xcopy "%SIGNDIR%\pe\scalar.service.dll" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
xcopy "%SIGNDIR%\macho\scalar" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
xcopy "%SIGNDIR%\macho\scalar.service" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
xcopy "%SIGNDIR%\macho\Scalar.app" "%LAYOUTDIR%\Library\Application Support\Scalar\Scalar.app\" /s/h/e/k/f/c/y

Просмотреть файл

@ -17,7 +17,4 @@ mkdir "%SIGNDIR%\pe"
mkdir "%SIGNDIR%\macho"
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.dll" "%SIGNDIR%\pe" /k/h/y
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.common.dll" "%SIGNDIR%\pe" /k/h/y
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.service.dll" "%SIGNDIR%\pe" /k/h/y
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar" "%SIGNDIR%\macho" /k/h/y
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.service" "%SIGNDIR%\macho" /k/h/y
xcopy "%LAYOUTDIR%\Library\Application Support\Scalar\Scalar.app" "%SIGNDIR%\macho\Scalar.app\" /s/h/e/k/f/c/y

Просмотреть файл

@ -11,12 +11,9 @@
<ItemGroup>
<FilesToSign Include="
$(OutDir)\pe\scalar.dll;
$(OutDir)\pe\scalar.common.dll;
$(OutDir)\pe\scalar.service.dll;" />
$(OutDir)\pe\scalar.common.dll;" />
<MacFilesToSign Include="
$(OutDir)\macho\scalar;
$(OutDir)\macho\scalar.service;
$(OutDir)\macho\Scalar.app;" />
$(OutDir)\macho\scalar;" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,51 +0,0 @@
using Moq;
using NUnit.Framework;
using Scalar.Common;
using Scalar.Common.Maintenance;
using Scalar.Service;
using Scalar.UnitTests.Mock.Common;
using Scalar.UnitTests.Mock.FileSystem;
using System.IO;
namespace Scalar.UnitTests.Service.Mac
{
[TestFixture]
public class MacScalarVerbRunnerTests
{
private const int ExpectedActiveUserId = 502;
private static readonly string ExpectedActiveRepoPath = Path.Combine(MockFileSystem.GetMockRoot(), "code", "repo2");
private MockTracer tracer;
private MockPlatform scalarPlatform;
[SetUp]
public void SetUp()
{
this.tracer = new MockTracer();
this.scalarPlatform = (MockPlatform)ScalarPlatform.Instance;
this.scalarPlatform.MockCurrentUser = ExpectedActiveUserId.ToString();
}
[TestCase]
public void CallMaintenance_LaunchesVerbUsingCorrectArgs()
{
MaintenanceTasks.Task task = MaintenanceTasks.Task.FetchCommitsAndTrees;
string taskVerbName = MaintenanceTasks.GetVerbTaskName(task);
string scalarBinPath = Path.Combine(this.scalarPlatform.Constants.ScalarBinDirectoryPath, this.scalarPlatform.Constants.ScalarExecutableName);
string expectedArgs =
$"run {taskVerbName} \"{ExpectedActiveRepoPath}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {new InternalVerbParameters(startedByService: true).ToJson()}";
Mock<MacScalarVerbRunner.ScalarProcessLauncher> procLauncherMock = new Mock<MacScalarVerbRunner.ScalarProcessLauncher>(MockBehavior.Strict, this.tracer);
procLauncherMock.Setup(mp => mp.LaunchProcess(
scalarBinPath,
expectedArgs,
ExpectedActiveRepoPath))
.Returns(new ProcessResult(output: string.Empty, errors: string.Empty, exitCode: 0));
MacScalarVerbRunner verbProcess = new MacScalarVerbRunner(this.tracer, procLauncherMock.Object);
verbProcess.CallMaintenance(task, ExpectedActiveRepoPath, ExpectedActiveUserId);
procLauncherMock.VerifyAll();
}
}
}

Просмотреть файл

@ -32,8 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scalar.Installer.Windows",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scalar.Service.UI", "Scalar.Service.UI\Scalar.Service.UI.csproj", "{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scalar.Notifications.Mac", "Scalar.Notifications.Mac\Scalar.Notifications.Mac.csproj", "{ED367118-BFB1-41CA-A010-E46C28796ED8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scalar.Packaging.Linux", "Scalar.Packaging.Linux\Scalar.Packaging.Linux.csproj", "{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}"
EndProject
Global
@ -86,10 +84,6 @@ Global
{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}.Release|Any CPU.Build.0 = Release|Any CPU
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Release|Any CPU.Build.0 = Release|Any CPU
{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}.Release|Any CPU.ActiveCfg = Release|Any CPU