diff --git a/CHANGELOG.md b/CHANGELOG.md index 64713e8..b158d0f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Refactor application. +## [0.7.0] - 2021-08-22 + +### Added + +- Added new classes `Path` for working with path's and creating relative path from specified directory. +- Add unit-tests cases for the `Path` class. + +### Changed + +- Change soft link creation using relative instead absolute path. + ## [0.6.0] - 2021-08-02 ### Changed diff --git a/README.md b/README.md index a224cac..e09474a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The Quick Symlink is a `Finder extension` which provides a `contextual menu item` for the symbolic links creation on macOS. -[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_07,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_20,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date) +[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date) [![license](https://img.shields.io/badge/license-MIT-informational?style=flat-square)](LICENSE) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg?style=flat-square)](CODE_OF_CONDUCT.md) diff --git a/TODO.md b/TODO.md index 3aee519..a3ffbc5 100755 --- a/TODO.md +++ b/TODO.md @@ -5,8 +5,9 @@ - [x] Develop the `Finder extension` which allows to create a symlinks for selected folders and files via contextual menu. - [x] Add the new menu item for replacing selected folders and files with symlinks. - [x] Add the new menu item for creating symlink in a parent directory (parent for target objects). -- [ ] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume) -- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu. +- [x] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume) +- [ ] Refactor code in `commons/*` part and add unit-tests +- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu - [ ] Develop the action panel for created symbolic links and hard lonks (in the app window): - [ ] a) (if broken) process to browse finder for 'Find/fix missing target' - [ ] b) Modify existing paths and symbolic link features diff --git a/commons/FileSystem/Path.swift b/commons/FileSystem/Path.swift new file mode 100644 index 0000000..cc1f269 --- /dev/null +++ b/commons/FileSystem/Path.swift @@ -0,0 +1,20 @@ +// +// Path.swift +// quick-symlink +// +// Created by Alexander A. Kropotin on 22/08/2021. +// Copyright © 2021 Alexander A. Kropotin. All rights reserved. +// + +import Foundation + +public protocol Path { + + func relativize(to other: Path!) -> Path!; + + func getPathFragments() -> [String]!; + + func toUrl() -> URL?; + + func toUriString() -> String?; +} diff --git a/commons/FileSystem/ResourcePath.swift b/commons/FileSystem/ResourcePath.swift new file mode 100644 index 0000000..fa4fb6c --- /dev/null +++ b/commons/FileSystem/ResourcePath.swift @@ -0,0 +1,82 @@ +// +// ResourcePath.swift +// quick-symlink +// +// Created by Alexander A. Kropotin on 22/08/2021. +// Copyright © 2021 Alexander A. Kropotin. All rights reserved. +// + +import Foundation + +public class ResourcePath: Path { + + public static func of(url: URL!) -> Path! { + return ResourcePath.of(fragments: url.pathComponents) + } + + public static func of(fragments: [String]!) -> Path! { + return ResourcePath.init(of: fragments); + } + + private var uriFragments: [String]!; + + public init(of fragments: [String]) { + self.uriFragments = fragments; + } + + public func getPathFragments() -> [String]! { + return self.uriFragments; + } + + public func relativize(to other: Path!) -> Path! { + var pathFragments = self.uriFragments; + var targetPathFragments = other.getPathFragments(); + + var destinationPath = URL.init(string: "./")!; + for targetPathFragment in targetPathFragments! { + if (!(pathFragments?.contains(targetPathFragment))!) { + break; + } + + pathFragments?.remove(at: 0); + targetPathFragments?.remove(at: 0); + } + + for _ in targetPathFragments! { + destinationPath.appendPathComponent("../"); + } + + for pathFragment in pathFragments! { + destinationPath.appendPathComponent(pathFragment); + } + + //pathFragments!.append(contentsOf: targetPathFragments!); + + return ResourcePath.of(url: destinationPath); + } + + public func toUrl() -> URL? { + if self.uriFragments.count == 0 { + return nil; + } + + var uri = URL.init(string: self.uriFragments.first!)!; + for fragment in self.uriFragments.dropFirst().dropLast() { + uri.appendPathComponent(fragment); + uri.appendPathComponent("/"); + } + uri.appendPathComponent(self.uriFragments.last!); + + return uri; + } + + public func toUriString() -> String? { + if self.uriFragments.count == 0 { + return nil; + } + + return self.toUrl()!.absoluteString; + } + + +} diff --git a/commons/CopyPathAction.swift b/commons/QuickSymlinkActions/CopyPathAction.swift similarity index 100% rename from commons/CopyPathAction.swift rename to commons/QuickSymlinkActions/CopyPathAction.swift diff --git a/commons/CreateLinkAction.swift b/commons/QuickSymlinkActions/CreateLinkAction.swift similarity index 88% rename from commons/CreateLinkAction.swift rename to commons/QuickSymlinkActions/CreateLinkAction.swift index 7f6dc76..ac97835 100644 --- a/commons/CreateLinkAction.swift +++ b/commons/QuickSymlinkActions/CreateLinkAction.swift @@ -29,7 +29,7 @@ public class CreateLinkAction: QuickSymlinkAction { let targetPath = self.getTargetPath(path, to: path.deletingLastPathComponent()); do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: path); + try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: path).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/commons/PasteLinkAction.swift b/commons/QuickSymlinkActions/PasteLinkAction.swift similarity index 90% rename from commons/PasteLinkAction.swift rename to commons/QuickSymlinkActions/PasteLinkAction.swift index b174aa1..991fbbe 100644 --- a/commons/PasteLinkAction.swift +++ b/commons/QuickSymlinkActions/PasteLinkAction.swift @@ -38,7 +38,7 @@ public class PasteLinkAction: QuickSymlinkAction { let targetPath = self.getTargetPath(pathUrl, to: target); do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: URL(fileURLWithPath: path)); + try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: pathUrl).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/commons/QuickSymlinkAction.swift b/commons/QuickSymlinkActions/QuickSymlinkAction.swift similarity index 100% rename from commons/QuickSymlinkAction.swift rename to commons/QuickSymlinkActions/QuickSymlinkAction.swift diff --git a/commons/ReplaceWithLinkAction.swift b/commons/QuickSymlinkActions/ReplaceWithLinkAction.swift similarity index 91% rename from commons/ReplaceWithLinkAction.swift rename to commons/QuickSymlinkActions/ReplaceWithLinkAction.swift index 07c988f..d2a9c1b 100644 --- a/commons/ReplaceWithLinkAction.swift +++ b/commons/QuickSymlinkActions/ReplaceWithLinkAction.swift @@ -39,7 +39,7 @@ public class ReplaceWithLinkAction: QuickSymlinkAction { do { try FileManager.default.moveItem(at: pathUrl, to: targetPath!); - try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: targetPath!); + try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: ResourcePath.of(url: targetPath).relativize(to: ResourcePath.of(url: pathUrl.deletingLastPathComponent())).toUrl()!); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/quick-symlink-tests/Info.plist b/quick-symlink-tests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/quick-symlink-tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/quick-symlink-tests/ResourcePathTest.swift b/quick-symlink-tests/ResourcePathTest.swift new file mode 100644 index 0000000..40f4b7e --- /dev/null +++ b/quick-symlink-tests/ResourcePathTest.swift @@ -0,0 +1,49 @@ +// +// ResourcePathTest.swift +// quick-symlink-tests +// +// Created by Alexander A. Kropotin on 22/08/2021. +// Copyright © 2021 Alexander A. Kropotin. All rights reserved. +// + +import XCTest + +class ResourcePathTest: XCTestCase { + + func test_toURL_methodExecution_returnedURLIsEqualToInitialURL() { + let uri: URL = URL.init(string: "/a/b/c/d")!; + let path: Path = ResourcePath.of(url: uri); + + XCTAssert(path.toUrl() == uri, "The Path URL is not equal to URL"); + } + + func test_relativize_whenCurrentDirectoryIsNestedToOtherDirectory_thenReturnPathStartingFromOtherDirectory() { + let currentUri: URL = URL.init(string: "/a/b/c/d")!; + let otherUri: URL = URL.init(string: "/a/b")!; + + let relativePath: Path = ResourcePath.of(url: currentUri) + .relativize(to: ResourcePath.of(url: otherUri)); + + XCTAssert(relativePath.toUriString() == "./c/d", "The relative path is wrong"); + } + + func test_relativize_whenOtherDirectoryIsNestedToCurrentDirectory_thenReturnPathWithOnlyJumpsAboveOtherDirectory() { + let currentUri: URL = URL.init(string: "/a/b")!; + let otherUri: URL = URL.init(string: "/a/b/c/d")!; + + let relativePath: Path = ResourcePath.of(url: currentUri) + .relativize(to: ResourcePath.of(url: otherUri)); + + XCTAssert(relativePath.toUriString() == "./../..", "The relative path is wrong"); + } + + func test_relativize_whenCurrentDirectoryAndOtherDirectoryAreNestedToGeneralDirectoryy_thenReturnPathWithJumpsAboveOtherDirectoryAndPartOfCurrentDirectory() { + let currentUri: URL = URL.init(string: "/a/b/c1/d1")!; + let otherUri: URL = URL.init(string: "/a/b/c2/d2")!; + + let relativePath: Path = ResourcePath.of(url: currentUri) + .relativize(to: ResourcePath.of(url: otherUri)); + + XCTAssert(relativePath.toUriString() == "./../../c1/d1", "The relative path is wrong"); + } +} diff --git a/quick-symlink.xcodeproj/project.pbxproj b/quick-symlink.xcodeproj/project.pbxproj index 7e1ab47..1de801c 100644 --- a/quick-symlink.xcodeproj/project.pbxproj +++ b/quick-symlink.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + A307B41726D21E39002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; + A307B41826D21E39002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; + A307B41A26D22116002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A307B41B26D22116002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A307B42F26D2563B002EEF58 /* ResourcePathTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B42E26D2563B002EEF58 /* ResourcePathTest.swift */; }; + A307B43026D257C5002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A307B43126D257CA002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; A30B9AAB265CA63300ACAA63 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A30B9AAA265CA63300ACAA63 /* AppDelegate.swift */; }; A30B9AAD265CA63300ACAA63 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A30B9AAC265CA63300ACAA63 /* ViewController.swift */; }; A30B9AAF265CA63300ACAA63 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A30B9AAE265CA63300ACAA63 /* Assets.xcassets */; }; @@ -28,6 +35,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + A307B42926D255FB002EEF58 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A30B9A9F265CA63300ACAA63 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A30B9AA6265CA63300ACAA63; + remoteInfo = "quick-symlink"; + }; A30B9AC4265CA68900ACAA63 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A30B9A9F265CA63300ACAA63 /* Project object */; @@ -52,6 +66,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A307B41626D21E39002EEF58 /* Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Path.swift; sourceTree = ""; }; + A307B41926D22116002EEF58 /* ResourcePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePath.swift; sourceTree = ""; }; + A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "quick-symlink-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + A307B42826D255FB002EEF58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A307B42E26D2563B002EEF58 /* ResourcePathTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePathTest.swift; sourceTree = ""; }; A30B9AA7265CA63300ACAA63 /* quick-symlink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "quick-symlink.app"; sourceTree = BUILT_PRODUCTS_DIR; }; A30B9AAA265CA63300ACAA63 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A30B9AAC265CA63300ACAA63 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -80,6 +99,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + A307B42126D255FB002EEF58 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A30B9AA4265CA63300ACAA63 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -97,6 +123,36 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A307B41426D21DBE002EEF58 /* QuickSymlinkActions */ = { + isa = PBXGroup; + children = ( + A316477626B7B403001DD969 /* CreateLinkAction.swift */, + A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */, + A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */, + A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */, + A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */, + ); + path = QuickSymlinkActions; + sourceTree = ""; + }; + A307B41526D21E23002EEF58 /* FileSystem */ = { + isa = PBXGroup; + children = ( + A307B41626D21E39002EEF58 /* Path.swift */, + A307B41926D22116002EEF58 /* ResourcePath.swift */, + ); + path = FileSystem; + sourceTree = ""; + }; + A307B42526D255FB002EEF58 /* quick-symlink-tests */ = { + isa = PBXGroup; + children = ( + A307B42826D255FB002EEF58 /* Info.plist */, + A307B42E26D2563B002EEF58 /* ResourcePathTest.swift */, + ); + path = "quick-symlink-tests"; + sourceTree = ""; + }; A30B9A9E265CA63300ACAA63 = { isa = PBXGroup; children = ( @@ -104,6 +160,7 @@ A3D93EB726A09B5A004E068D /* Localizable.strings */, A30B9AA9265CA63300ACAA63 /* quick-symlink */, A30B9ABF265CA68900ACAA63 /* quick-symlink-extension */, + A307B42526D255FB002EEF58 /* quick-symlink-tests */, A30B9AA8265CA63300ACAA63 /* Products */, ); sourceTree = ""; @@ -113,6 +170,7 @@ children = ( A30B9AA7265CA63300ACAA63 /* quick-symlink.app */, A30B9ABE265CA68900ACAA63 /* quick-symlink-extension.appex */, + A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */, ); name = Products; sourceTree = ""; @@ -145,11 +203,8 @@ A345C9BD26A0B12C004FBF0F /* commons */ = { isa = PBXGroup; children = ( - A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */, - A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */, - A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */, - A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */, - A316477626B7B403001DD969 /* CreateLinkAction.swift */, + A307B41526D21E23002EEF58 /* FileSystem */, + A307B41426D21DBE002EEF58 /* QuickSymlinkActions */, ); path = commons; sourceTree = ""; @@ -157,6 +212,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + A307B42326D255FB002EEF58 /* quick-symlink-tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A307B42B26D255FB002EEF58 /* Build configuration list for PBXNativeTarget "quick-symlink-tests" */; + buildPhases = ( + A307B42026D255FB002EEF58 /* Sources */, + A307B42126D255FB002EEF58 /* Frameworks */, + A307B42226D255FB002EEF58 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A307B42A26D255FB002EEF58 /* PBXTargetDependency */, + ); + name = "quick-symlink-tests"; + productName = "quick-symlink-tests"; + productReference = A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; A30B9AA6265CA63300ACAA63 /* quick-symlink */ = { isa = PBXNativeTarget; buildConfigurationList = A30B9AB7265CA63300ACAA63 /* Build configuration list for PBXNativeTarget "quick-symlink" */; @@ -203,6 +276,11 @@ LastUpgradeCheck = 0920; ORGANIZATIONNAME = "Alexander A. Kropotin"; TargetAttributes = { + A307B42326D255FB002EEF58 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + TestTargetID = A30B9AA6265CA63300ACAA63; + }; A30B9AA6265CA63300ACAA63 = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; @@ -235,11 +313,19 @@ targets = ( A30B9AA6265CA63300ACAA63 /* quick-symlink */, A30B9ABD265CA68900ACAA63 /* quick-symlink-extension */, + A307B42326D255FB002EEF58 /* quick-symlink-tests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + A307B42226D255FB002EEF58 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A30B9AA5265CA63300ACAA63 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -261,12 +347,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + A307B42026D255FB002EEF58 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A307B43026D257C5002EEF58 /* ResourcePath.swift in Sources */, + A307B42F26D2563B002EEF58 /* ResourcePathTest.swift in Sources */, + A307B43126D257CA002EEF58 /* Path.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A30B9AA3265CA63300ACAA63 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A307B41A26D22116002EEF58 /* ResourcePath.swift in Sources */, A345C9C826A0B552004FBF0F /* ReplaceWithLinkAction.swift in Sources */, A316477726B7B403001DD969 /* CreateLinkAction.swift in Sources */, + A307B41726D21E39002EEF58 /* Path.swift in Sources */, A30B9AAD265CA63300ACAA63 /* ViewController.swift in Sources */, A345C9C226A0B30F004FBF0F /* CopyPathAction.swift in Sources */, A345C9BF26A0B200004FBF0F /* QuickSymlinkAction.swift in Sources */, @@ -280,6 +378,8 @@ buildActionMask = 2147483647; files = ( A30B9AC1265CA68900ACAA63 /* FinderSync.swift in Sources */, + A307B41826D21E39002EEF58 /* Path.swift in Sources */, + A307B41B26D22116002EEF58 /* ResourcePath.swift in Sources */, A30D4A4B26A0C14400BA775B /* PasteLinkAction.swift in Sources */, A30D4A4C26A0C14400BA775B /* ReplaceWithLinkAction.swift in Sources */, A316477826B7B403001DD969 /* CreateLinkAction.swift in Sources */, @@ -291,6 +391,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + A307B42A26D255FB002EEF58 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A30B9AA6265CA63300ACAA63 /* quick-symlink */; + targetProxy = A307B42926D255FB002EEF58 /* PBXContainerItemProxy */; + }; A30B9AC5265CA68900ACAA63 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A30B9ABD265CA68900ACAA63 /* quick-symlink-extension */; @@ -325,6 +430,38 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + A307B42C26D255FB002EEF58 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "quick-symlink-tests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/quick-symlink.app/Contents/MacOS/quick-symlink"; + }; + name = Debug; + }; + A307B42D26D255FB002EEF58 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "quick-symlink-tests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/quick-symlink.app/Contents/MacOS/quick-symlink"; + }; + name = Release; + }; A30B9AB5265CA63300ACAA63 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -502,6 +639,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + A307B42B26D255FB002EEF58 /* Build configuration list for PBXNativeTarget "quick-symlink-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A307B42C26D255FB002EEF58 /* Debug */, + A307B42D26D255FB002EEF58 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A30B9AA2265CA63300ACAA63 /* Build configuration list for PBXProject "quick-symlink" */ = { isa = XCConfigurationList; buildConfigurations = (