From 9ee06505a2ef12644d6b55179ee1abc4b201cbfb Mon Sep 17 00:00:00 2001 From: Marcin Iwanicki Date: Tue, 7 Jul 2020 08:32:20 +0100 Subject: [PATCH] Add dependencies comparator (#44) Resolves: https://github.com/bloomberg/xcdiff/issues/41 **Describe your changes** Added new comparator to compare "Dependencies" build phases. Usage: ```sh xcdiff -g dependencies ``` **Testing performed** - Compare the same project to itself - Run `xcdiff -p1 Fixtures/ios_project_1/Project.xcodeproj -p2 Fixtures/ios_project_1/Project.xcodeproj -g dependencies ` - Verify no differences are flagged - Compare two different projects - Run `xcdiff -p1 Fixtures/ios_project_1/Project.xcodeproj -p2 Fixtures/ios_project_2/Project.xcodeproj -g dependencies ` - Verify dependencies differences are flagged correctly --- .../Generated/differences_only.2.42311ee5.md | 1 + .../Generated/html_format.2.e5ef416a.md | 2 +- .../html_format_verbose.2.3ef640ed.md | 2 +- CommandTests/Generated/list.0.a330395c.md | 1 + CommandTests/Generated/p1_p2.2.bac5a245.md | 5 + .../p1_p2_Project_target.2.961f27cf.md | 1 + ...roject_target_console_format.2.dfbdb9cf.md | 1 + ...arget_console_format_verbose.2.ba4ce842.md | 8 + ...2_Project_target_json_format.2.1b44ee6e.md | 16 + ...t_target_json_format_verbose.2.bcad53ce.md | 16 + ...oject_target_markdown_format.2.d851dd25.md | 3 + ...rget_markdown_format_verbose.2.f582a13e.md | 10 + ...p1_p2_Project_target_verbose.2.6f518e43.md | 8 + .../p1_p2_console_format.2.9841a8d7.md | 5 + ...p1_p2_console_format_verbose.2.40a241bd.md | 12 + .../Generated/p1_p2_json_format.2.e54c06ce.md | 76 ++++ .../p1_p2_json_format_verbose.2.0e0a3eb6.md | 76 ++++ .../p1_p2_markdown_format.2.1e09644b.md | 15 + ...1_p2_markdown_format_verbose.2.ac528bab.md | 22 ++ .../Generated/p1_p2_verbose.2.fe666557.md | 12 + Documentation/Comparators.md | 6 +- .../Comparator/DependenciesComparator.swift | 142 ++++++++ .../LinkedDependenciesComparator.swift | 18 +- Sources/XCDiffCore/ComparatorType.swift | 5 + .../XCDiffCore/Library/TargetsHelper.swift | 14 +- .../CommandsRunnerTests.swift | 5 + .../Comparator/ComparatorTypeTests.swift | 1 + .../DependenciesComparatorTests.swift | 331 ++++++++++++++++++ .../Helpers/PBXNativeTargetBuilder.swift | 9 +- .../Helpers/PBXProjBuilder.swift | 99 ++++++ 30 files changed, 899 insertions(+), 23 deletions(-) create mode 100644 Sources/XCDiffCore/Comparator/DependenciesComparator.swift create mode 100644 Tests/XCDiffCoreTests/Comparator/DependenciesComparatorTests.swift diff --git a/CommandTests/Generated/differences_only.2.42311ee5.md b/CommandTests/Generated/differences_only.2.42311ee5.md index 7fee0c1..55c034f 100644 --- a/CommandTests/Generated/differences_only.2.42311ee5.md +++ b/CommandTests/Generated/differences_only.2.42311ee5.md @@ -33,6 +33,7 @@ ❌ SETTINGS > "ProjectFramework" target > "Debug" configuration > Values ❌ SETTINGS > "ProjectFramework" target > "Release" configuration > Values ❌ SOURCE_TREES > Root project +❌ DEPENDENCIES > "Project" target ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/CommandTests/Generated/html_format.2.e5ef416a.md b/CommandTests/Generated/html_format.2.e5ef416a.md index 3c2e705..94a7206 100644 --- a/CommandTests/Generated/html_format.2.e5ef416a.md +++ b/CommandTests/Generated/html_format.2.e5ef416a.md @@ -104,7 +104,7 @@

Δ xcdiff result

-

❌ FILE_REFERENCES

❌ BUILD_PHASES > "MismatchingLibrary" target

✅ BUILD_PHASES > "Project" target

✅ BUILD_PHASES > "ProjectFramework" target

✅ BUILD_PHASES > "ProjectTests" target

✅ BUILD_PHASES > "ProjectUITests" target

✅ COPY_FILES > "MismatchingLibrary" target

❌ COPY_FILES > "Project" target > Embed Frameworks

✅ COPY_FILES > "ProjectFramework" target

✅ COPY_FILES > "ProjectTests" target

✅ COPY_FILES > "ProjectUITests" target

❌ TARGETS > NATIVE targets

❌ TARGETS > AGGREGATE targets

❌ HEADERS > "MismatchingLibrary" target

✅ HEADERS > "Project" target

❌ HEADERS > "ProjectFramework" target

✅ HEADERS > "ProjectTests" target

✅ HEADERS > "ProjectUITests" target

✅ SOURCES > "MismatchingLibrary" target

❌ SOURCES > "Project" target

✅ SOURCES > "ProjectFramework" target

❌ SOURCES > "ProjectTests" target

❌ SOURCES > "ProjectUITests" target

✅ RESOURCES > "MismatchingLibrary" target

❌ RESOURCES > "Project" target

✅ RESOURCES > "ProjectFramework" target

❌ RESOURCES > "ProjectTests" target

❌ RESOURCES > "ProjectUITests" target

❌ CONFIGURATIONS > Root project

❌ SETTINGS > Root project > "Debug" configuration > Base configuration

❌ SETTINGS > Root project > "Debug" configuration > Values

✅ SETTINGS > Root project > "Release" configuration > Base configuration

❌ SETTINGS > Root project > "Release" configuration > Values

✅ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Values

✅ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Values

❌ SETTINGS > "Project" target > "Debug" configuration > Base configuration

❌ SETTINGS > "Project" target > "Debug" configuration > Values

✅ SETTINGS > "Project" target > "Release" configuration > Base configuration

❌ SETTINGS > "Project" target > "Release" configuration > Values

✅ SETTINGS > "ProjectFramework" target > "Debug" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectFramework" target > "Release" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Release" configuration > Values

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values

❌ SOURCE_TREES > Root project

✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target

❌ LINKED_DEPENDENCIES > "Project" target

✅ LINKED_DEPENDENCIES > "ProjectFramework" target

✅ LINKED_DEPENDENCIES > "ProjectTests" target

✅ LINKED_DEPENDENCIES > "ProjectUITests" target

+

❌ FILE_REFERENCES

❌ BUILD_PHASES > "MismatchingLibrary" target

✅ BUILD_PHASES > "Project" target

✅ BUILD_PHASES > "ProjectFramework" target

✅ BUILD_PHASES > "ProjectTests" target

✅ BUILD_PHASES > "ProjectUITests" target

✅ COPY_FILES > "MismatchingLibrary" target

❌ COPY_FILES > "Project" target > Embed Frameworks

✅ COPY_FILES > "ProjectFramework" target

✅ COPY_FILES > "ProjectTests" target

✅ COPY_FILES > "ProjectUITests" target

❌ TARGETS > NATIVE targets

❌ TARGETS > AGGREGATE targets

❌ HEADERS > "MismatchingLibrary" target

✅ HEADERS > "Project" target

❌ HEADERS > "ProjectFramework" target

✅ HEADERS > "ProjectTests" target

✅ HEADERS > "ProjectUITests" target

✅ SOURCES > "MismatchingLibrary" target

❌ SOURCES > "Project" target

✅ SOURCES > "ProjectFramework" target

❌ SOURCES > "ProjectTests" target

❌ SOURCES > "ProjectUITests" target

✅ RESOURCES > "MismatchingLibrary" target

❌ RESOURCES > "Project" target

✅ RESOURCES > "ProjectFramework" target

❌ RESOURCES > "ProjectTests" target

❌ RESOURCES > "ProjectUITests" target

❌ CONFIGURATIONS > Root project

❌ SETTINGS > Root project > "Debug" configuration > Base configuration

❌ SETTINGS > Root project > "Debug" configuration > Values

✅ SETTINGS > Root project > "Release" configuration > Base configuration

❌ SETTINGS > Root project > "Release" configuration > Values

✅ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Values

✅ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Values

❌ SETTINGS > "Project" target > "Debug" configuration > Base configuration

❌ SETTINGS > "Project" target > "Debug" configuration > Values

✅ SETTINGS > "Project" target > "Release" configuration > Base configuration

❌ SETTINGS > "Project" target > "Release" configuration > Values

✅ SETTINGS > "ProjectFramework" target > "Debug" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectFramework" target > "Release" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Release" configuration > Values

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values

❌ SOURCE_TREES > Root project

✅ DEPENDENCIES > "MismatchingLibrary" target

❌ DEPENDENCIES > "Project" target

✅ DEPENDENCIES > "ProjectFramework" target

✅ DEPENDENCIES > "ProjectTests" target

✅ DEPENDENCIES > "ProjectUITests" target

✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target

❌ LINKED_DEPENDENCIES > "Project" target

✅ LINKED_DEPENDENCIES > "ProjectFramework" target

✅ LINKED_DEPENDENCIES > "ProjectTests" target

✅ LINKED_DEPENDENCIES > "ProjectUITests" target

diff --git a/CommandTests/Generated/html_format_verbose.2.3ef640ed.md b/CommandTests/Generated/html_format_verbose.2.3ef640ed.md index 1151144..ce7e5d4 100644 --- a/CommandTests/Generated/html_format_verbose.2.3ef640ed.md +++ b/CommandTests/Generated/html_format_verbose.2.3ef640ed.md @@ -104,7 +104,7 @@

Δ xcdiff result

-

❌ FILE_REFERENCES

⚠️ Only in first (8):

  • Project/Group B/AViewController.xib
  • Project/Group B/AnotherObjcClass.h
  • Project/Group B/AnotherObjcClass.m
  • Project/Resources/time.png
  • ProjectTests/BarTests.swift
  • ProjectUITests/LoginTests.swift
  • ProjectUITests/Screenshots/empty.png
  • libMismatchingLibrary.a

⚠️ Only in second (11):

  • MismatchingLibrary.framework
  • MismatchingLibrary/MismatchingLibrary-Info.plist
  • NewFramework.framework
  • NewFramework/Info.plist
  • NewFramework/NewFramework.h
  • Project/Project.xcconfig
  • Project/Target.xcconfig
  • ProjectFramework/Header4.h
  • ProjectTests/Responses/ListResponse.json
  • ProjectUITests/MetricsTests.swift
  • README.md

❌ BUILD_PHASES > "MismatchingLibrary" target

⚠️ Only in first (1):

  • CopyFiles

⚠️ Only in second (2):

  • Headers
  • Resources

✅ BUILD_PHASES > "Project" target

✅ BUILD_PHASES > "ProjectFramework" target

✅ BUILD_PHASES > "ProjectTests" target

✅ BUILD_PHASES > "ProjectUITests" target

✅ COPY_FILES > "MismatchingLibrary" target

❌ COPY_FILES > "Project" target > Embed Frameworks

⚠️ Only in second (2):

  • MismatchingLibrary.framework
  • NewFramework.framework

⚠️ Value mismatch (1):

  • ProjectFramework.framework

    • attributes = ["CodeSignOnCopy", "RemoveHeadersOnCopy"]
    • attributes = []

✅ COPY_FILES > "ProjectFramework" target

✅ COPY_FILES > "ProjectTests" target

✅ COPY_FILES > "ProjectUITests" target

❌ TARGETS > NATIVE targets

⚠️ Only in second (1):

  • NewFramework

⚠️ Value mismatch (1):

  • MismatchingLibrary product type

    • com.apple.product-type.library.static
    • com.apple.product-type.framework

❌ TARGETS > AGGREGATE targets

⚠️ Only in second (1):

  • NewAggregate

❌ HEADERS > "MismatchingLibrary" target

⚠️ Only in second (1):

  • MismatchingLibrary/MismatchingLibrary.h

✅ HEADERS > "Project" target

❌ HEADERS > "ProjectFramework" target

⚠️ Only in second (1):

  • ProjectFramework/Header4.h

⚠️ Value mismatch (2):

  • ProjectFramework/Header1.h attributes

    • Public
    • nil (Project)
  • ProjectFramework/Header2.h attributes

    • Private
    • nil (Project)

✅ HEADERS > "ProjectTests" target

✅ HEADERS > "ProjectUITests" target

✅ SOURCES > "MismatchingLibrary" target

❌ SOURCES > "Project" target

⚠️ Only in first (1):

  • Project/Group B/AnotherObjcClass.m

⚠️ Value mismatch (1):

  • Project/Group A/ObjcClass.m compiler flags

    • nil
    • -ObjC

✅ SOURCES > "ProjectFramework" target

❌ SOURCES > "ProjectTests" target

⚠️ Only in first (1):

  • ProjectTests/BarTests.swift

❌ SOURCES > "ProjectUITests" target

⚠️ Only in first (1):

  • ProjectUITests/LoginTests.swift

⚠️ Only in second (1):

  • ProjectUITests/MetricsTests.swift

✅ RESOURCES > "MismatchingLibrary" target

❌ RESOURCES > "Project" target

⚠️ Only in first (2):

  • Project/Group B/AViewController.xib
  • Project/Resources/time.png

✅ RESOURCES > "ProjectFramework" target

❌ RESOURCES > "ProjectTests" target

⚠️ Only in second (1):

  • ProjectTests/Responses/ListResponse.json

❌ RESOURCES > "ProjectUITests" target

⚠️ Only in first (1):

  • ProjectUITests/Screenshots/empty.png

❌ CONFIGURATIONS > Root project

⚠️ Only in second (1):

  • CUSTOM_NEW

❌ SETTINGS > Root project > "Debug" configuration > Base configuration

⚠️ Value mismatch (1):

  • Path to .xcconfig

    • nil
    • Project/Project.xcconfig

❌ SETTINGS > Root project > "Debug" configuration > Values

⚠️ Only in second (1):

  • CUSTOM_SETTGING_1 = CS_1_PROJECT_LEVEL

✅ SETTINGS > Root project > "Release" configuration > Base configuration

❌ SETTINGS > Root project > "Release" configuration > Values

⚠️ Only in second (1):

  • CUSTOM_SETTGING_1 = CS_1_PROJECT_LEVEL

✅ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Values

⚠️ Only in first (1):

  • OTHER_LDFLAGS = -ObjC

⚠️ Only in second (13):

  • CLANG_ENABLE_MODULES = YES
  • CURRENT_PROJECT_VERSION = 1
  • DEFINES_MODULE = YES
  • DYLIB_COMPATIBILITY_VERSION = 1
  • DYLIB_CURRENT_VERSION = 1
  • DYLIB_INSTALL_NAME_BASE = @rpath
  • INFOPLIST_FILE = MismatchingLibrary/MismatchingLibrary-Info.plist
  • INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
  • LD_RUNPATH_SEARCH_PATHS = ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"]
  • PRODUCT_BUNDLE_IDENTIFIER = com.bloomberg.xcdiff.Project.MismatchingLibrary
  • SWIFT_OPTIMIZATION_LEVEL = -Onone
  • VERSIONING_SYSTEM = apple-generic
  • VERSION_INFO_PREFIX =

⚠️ Value mismatch (1):

  • PRODUCT_NAME

    • $(TARGET_NAME)
    • $(TARGET_NAME:c99extidentifier)

✅ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Values

⚠️ Only in first (1):

  • OTHER_LDFLAGS = -ObjC

⚠️ Only in second (12):

  • CLANG_ENABLE_MODULES = YES
  • CURRENT_PROJECT_VERSION = 1
  • DEFINES_MODULE = YES
  • DYLIB_COMPATIBILITY_VERSION = 1
  • DYLIB_CURRENT_VERSION = 1
  • DYLIB_INSTALL_NAME_BASE = @rpath
  • INFOPLIST_FILE = MismatchingLibrary/MismatchingLibrary-Info.plist
  • INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
  • LD_RUNPATH_SEARCH_PATHS = ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"]
  • PRODUCT_BUNDLE_IDENTIFIER = com.bloomberg.xcdiff.Project.MismatchingLibrary
  • VERSIONING_SYSTEM = apple-generic
  • VERSION_INFO_PREFIX =

⚠️ Value mismatch (1):

  • PRODUCT_NAME

    • $(TARGET_NAME)
    • $(TARGET_NAME:c99extidentifier)

❌ SETTINGS > "Project" target > "Debug" configuration > Base configuration

⚠️ Value mismatch (1):

  • Path to .xcconfig

    • nil
    • Project/Target.xcconfig

❌ SETTINGS > "Project" target > "Debug" configuration > Values

⚠️ Only in second (1):

  • ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES

⚠️ Value mismatch (1):

  • CUSTOM_SETTING_COMMON

    • VALUE_1
    • VALUE_2

✅ SETTINGS > "Project" target > "Release" configuration > Base configuration

❌ SETTINGS > "Project" target > "Release" configuration > Values

⚠️ Only in second (1):

  • ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES

⚠️ Value mismatch (1):

  • CUSTOM_SETTING_COMMON

    • VALUE_1
    • VALUE_2

✅ SETTINGS > "ProjectFramework" target > "Debug" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Debug" configuration > Values

⚠️ Value mismatch (1):

  • PRODUCT_BUNDLE_IDENTIFIER

    • com.bloomberg.xcdiff.Project.testprovisioning.ProjectFramework
    • com.bloomberg.xcdiff.Project.ProjectFramework

✅ SETTINGS > "ProjectFramework" target > "Release" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Release" configuration > Values

⚠️ Value mismatch (1):

  • PRODUCT_BUNDLE_IDENTIFIER

    • com.bloomberg.xcdiff.Project.testprovisioning.ProjectFramework
    • com.bloomberg.xcdiff.Project.ProjectFramework

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values

❌ SOURCE_TREES > Root project

Output format: (<path>, <name>, <source_tree>)

⚠️ Only in first (8):

  • (AViewController.xib, nil, <group>) → (Group B, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (AnotherObjcClass.h, nil, <group>) → (Group B, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (AnotherObjcClass.m, nil, <group>) → (Group B, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (BarTests.swift, nil, <group>) → (ProjectTests, nil, <group>) → (nil, nil, <group>)
  • (LoginTests.swift, nil, <group>) → (ProjectUITests, nil, <group>) → (nil, nil, <group>)
  • (empty.png, nil, <group>) → (Screenshots, nil, <group>) → (ProjectUITests, nil, <group>) → (nil, nil, <group>)
  • (libMismatchingLibrary.a, nil, BUILT_PRODUCTS_DIR) → (nil, Products, <group>) → (nil, nil, <group>)
  • (time.png, nil, <group>) → (Resources, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)

⚠️ Only in second (11):

  • (Header4.h, nil, <group>) → (ProjectFramework, nil, <group>) → (nil, nil, <group>)
  • (Info.plist, nil, <group>) → (NewFramework, nil, <group>) → (nil, nil, <group>)
  • (ListResponse.json, nil, <group>) → (Responses, nil, <group>) → (ProjectTests, nil, <group>) → (nil, nil, <group>)
  • (MetricsTests.swift, nil, <group>) → (ProjectUITests, nil, <group>) → (nil, nil, <group>)
  • (MismatchingLibrary-Info.plist, nil, <group>) → (MismatchingLibrary, nil, <group>) → (nil, nil, <group>)
  • (MismatchingLibrary.framework, nil, BUILT_PRODUCTS_DIR) → (nil, Products, <group>) → (nil, nil, <group>)
  • (NewFramework.framework, nil, BUILT_PRODUCTS_DIR) → (nil, Products, <group>) → (nil, nil, <group>)
  • (NewFramework.h, nil, <group>) → (NewFramework, nil, <group>) → (nil, nil, <group>)
  • (Project.xcconfig, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (README.md, nil, <group>) → (nil, nil, <group>)
  • (Target.xcconfig, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)

✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target

❌ LINKED_DEPENDENCIES > "Project" target

⚠️ Only in second (2):

  • MismatchingLibrary.framework
  • NewFramework.framework

⚠️ Value mismatch (1):

  • ARKit.framework attributes

    • required
    • optional

✅ LINKED_DEPENDENCIES > "ProjectFramework" target

✅ LINKED_DEPENDENCIES > "ProjectTests" target

✅ LINKED_DEPENDENCIES > "ProjectUITests" target

+

❌ FILE_REFERENCES

⚠️ Only in first (8):

  • Project/Group B/AViewController.xib
  • Project/Group B/AnotherObjcClass.h
  • Project/Group B/AnotherObjcClass.m
  • Project/Resources/time.png
  • ProjectTests/BarTests.swift
  • ProjectUITests/LoginTests.swift
  • ProjectUITests/Screenshots/empty.png
  • libMismatchingLibrary.a

⚠️ Only in second (11):

  • MismatchingLibrary.framework
  • MismatchingLibrary/MismatchingLibrary-Info.plist
  • NewFramework.framework
  • NewFramework/Info.plist
  • NewFramework/NewFramework.h
  • Project/Project.xcconfig
  • Project/Target.xcconfig
  • ProjectFramework/Header4.h
  • ProjectTests/Responses/ListResponse.json
  • ProjectUITests/MetricsTests.swift
  • README.md

❌ BUILD_PHASES > "MismatchingLibrary" target

⚠️ Only in first (1):

  • CopyFiles

⚠️ Only in second (2):

  • Headers
  • Resources

✅ BUILD_PHASES > "Project" target

✅ BUILD_PHASES > "ProjectFramework" target

✅ BUILD_PHASES > "ProjectTests" target

✅ BUILD_PHASES > "ProjectUITests" target

✅ COPY_FILES > "MismatchingLibrary" target

❌ COPY_FILES > "Project" target > Embed Frameworks

⚠️ Only in second (2):

  • MismatchingLibrary.framework
  • NewFramework.framework

⚠️ Value mismatch (1):

  • ProjectFramework.framework

    • attributes = ["CodeSignOnCopy", "RemoveHeadersOnCopy"]
    • attributes = []

✅ COPY_FILES > "ProjectFramework" target

✅ COPY_FILES > "ProjectTests" target

✅ COPY_FILES > "ProjectUITests" target

❌ TARGETS > NATIVE targets

⚠️ Only in second (1):

  • NewFramework

⚠️ Value mismatch (1):

  • MismatchingLibrary product type

    • com.apple.product-type.library.static
    • com.apple.product-type.framework

❌ TARGETS > AGGREGATE targets

⚠️ Only in second (1):

  • NewAggregate

❌ HEADERS > "MismatchingLibrary" target

⚠️ Only in second (1):

  • MismatchingLibrary/MismatchingLibrary.h

✅ HEADERS > "Project" target

❌ HEADERS > "ProjectFramework" target

⚠️ Only in second (1):

  • ProjectFramework/Header4.h

⚠️ Value mismatch (2):

  • ProjectFramework/Header1.h attributes

    • Public
    • nil (Project)
  • ProjectFramework/Header2.h attributes

    • Private
    • nil (Project)

✅ HEADERS > "ProjectTests" target

✅ HEADERS > "ProjectUITests" target

✅ SOURCES > "MismatchingLibrary" target

❌ SOURCES > "Project" target

⚠️ Only in first (1):

  • Project/Group B/AnotherObjcClass.m

⚠️ Value mismatch (1):

  • Project/Group A/ObjcClass.m compiler flags

    • nil
    • -ObjC

✅ SOURCES > "ProjectFramework" target

❌ SOURCES > "ProjectTests" target

⚠️ Only in first (1):

  • ProjectTests/BarTests.swift

❌ SOURCES > "ProjectUITests" target

⚠️ Only in first (1):

  • ProjectUITests/LoginTests.swift

⚠️ Only in second (1):

  • ProjectUITests/MetricsTests.swift

✅ RESOURCES > "MismatchingLibrary" target

❌ RESOURCES > "Project" target

⚠️ Only in first (2):

  • Project/Group B/AViewController.xib
  • Project/Resources/time.png

✅ RESOURCES > "ProjectFramework" target

❌ RESOURCES > "ProjectTests" target

⚠️ Only in second (1):

  • ProjectTests/Responses/ListResponse.json

❌ RESOURCES > "ProjectUITests" target

⚠️ Only in first (1):

  • ProjectUITests/Screenshots/empty.png

❌ CONFIGURATIONS > Root project

⚠️ Only in second (1):

  • CUSTOM_NEW

❌ SETTINGS > Root project > "Debug" configuration > Base configuration

⚠️ Value mismatch (1):

  • Path to .xcconfig

    • nil
    • Project/Project.xcconfig

❌ SETTINGS > Root project > "Debug" configuration > Values

⚠️ Only in second (1):

  • CUSTOM_SETTGING_1 = CS_1_PROJECT_LEVEL

✅ SETTINGS > Root project > "Release" configuration > Base configuration

❌ SETTINGS > Root project > "Release" configuration > Values

⚠️ Only in second (1):

  • CUSTOM_SETTGING_1 = CS_1_PROJECT_LEVEL

✅ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Debug" configuration > Values

⚠️ Only in first (1):

  • OTHER_LDFLAGS = -ObjC

⚠️ Only in second (13):

  • CLANG_ENABLE_MODULES = YES
  • CURRENT_PROJECT_VERSION = 1
  • DEFINES_MODULE = YES
  • DYLIB_COMPATIBILITY_VERSION = 1
  • DYLIB_CURRENT_VERSION = 1
  • DYLIB_INSTALL_NAME_BASE = @rpath
  • INFOPLIST_FILE = MismatchingLibrary/MismatchingLibrary-Info.plist
  • INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
  • LD_RUNPATH_SEARCH_PATHS = ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"]
  • PRODUCT_BUNDLE_IDENTIFIER = com.bloomberg.xcdiff.Project.MismatchingLibrary
  • SWIFT_OPTIMIZATION_LEVEL = -Onone
  • VERSIONING_SYSTEM = apple-generic
  • VERSION_INFO_PREFIX =

⚠️ Value mismatch (1):

  • PRODUCT_NAME

    • $(TARGET_NAME)
    • $(TARGET_NAME:c99extidentifier)

✅ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Base configuration

❌ SETTINGS > "MismatchingLibrary" target > "Release" configuration > Values

⚠️ Only in first (1):

  • OTHER_LDFLAGS = -ObjC

⚠️ Only in second (12):

  • CLANG_ENABLE_MODULES = YES
  • CURRENT_PROJECT_VERSION = 1
  • DEFINES_MODULE = YES
  • DYLIB_COMPATIBILITY_VERSION = 1
  • DYLIB_CURRENT_VERSION = 1
  • DYLIB_INSTALL_NAME_BASE = @rpath
  • INFOPLIST_FILE = MismatchingLibrary/MismatchingLibrary-Info.plist
  • INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
  • LD_RUNPATH_SEARCH_PATHS = ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"]
  • PRODUCT_BUNDLE_IDENTIFIER = com.bloomberg.xcdiff.Project.MismatchingLibrary
  • VERSIONING_SYSTEM = apple-generic
  • VERSION_INFO_PREFIX =

⚠️ Value mismatch (1):

  • PRODUCT_NAME

    • $(TARGET_NAME)
    • $(TARGET_NAME:c99extidentifier)

❌ SETTINGS > "Project" target > "Debug" configuration > Base configuration

⚠️ Value mismatch (1):

  • Path to .xcconfig

    • nil
    • Project/Target.xcconfig

❌ SETTINGS > "Project" target > "Debug" configuration > Values

⚠️ Only in second (1):

  • ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES

⚠️ Value mismatch (1):

  • CUSTOM_SETTING_COMMON

    • VALUE_1
    • VALUE_2

✅ SETTINGS > "Project" target > "Release" configuration > Base configuration

❌ SETTINGS > "Project" target > "Release" configuration > Values

⚠️ Only in second (1):

  • ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES

⚠️ Value mismatch (1):

  • CUSTOM_SETTING_COMMON

    • VALUE_1
    • VALUE_2

✅ SETTINGS > "ProjectFramework" target > "Debug" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Debug" configuration > Values

⚠️ Value mismatch (1):

  • PRODUCT_BUNDLE_IDENTIFIER

    • com.bloomberg.xcdiff.Project.testprovisioning.ProjectFramework
    • com.bloomberg.xcdiff.Project.ProjectFramework

✅ SETTINGS > "ProjectFramework" target > "Release" configuration > Base configuration

❌ SETTINGS > "ProjectFramework" target > "Release" configuration > Values

⚠️ Value mismatch (1):

  • PRODUCT_BUNDLE_IDENTIFIER

    • com.bloomberg.xcdiff.Project.testprovisioning.ProjectFramework
    • com.bloomberg.xcdiff.Project.ProjectFramework

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectTests" target > "Release" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Debug" configuration > Values

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration

✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values

❌ SOURCE_TREES > Root project

Output format: (<path>, <name>, <source_tree>)

⚠️ Only in first (8):

  • (AViewController.xib, nil, <group>) → (Group B, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (AnotherObjcClass.h, nil, <group>) → (Group B, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (AnotherObjcClass.m, nil, <group>) → (Group B, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (BarTests.swift, nil, <group>) → (ProjectTests, nil, <group>) → (nil, nil, <group>)
  • (LoginTests.swift, nil, <group>) → (ProjectUITests, nil, <group>) → (nil, nil, <group>)
  • (empty.png, nil, <group>) → (Screenshots, nil, <group>) → (ProjectUITests, nil, <group>) → (nil, nil, <group>)
  • (libMismatchingLibrary.a, nil, BUILT_PRODUCTS_DIR) → (nil, Products, <group>) → (nil, nil, <group>)
  • (time.png, nil, <group>) → (Resources, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)

⚠️ Only in second (11):

  • (Header4.h, nil, <group>) → (ProjectFramework, nil, <group>) → (nil, nil, <group>)
  • (Info.plist, nil, <group>) → (NewFramework, nil, <group>) → (nil, nil, <group>)
  • (ListResponse.json, nil, <group>) → (Responses, nil, <group>) → (ProjectTests, nil, <group>) → (nil, nil, <group>)
  • (MetricsTests.swift, nil, <group>) → (ProjectUITests, nil, <group>) → (nil, nil, <group>)
  • (MismatchingLibrary-Info.plist, nil, <group>) → (MismatchingLibrary, nil, <group>) → (nil, nil, <group>)
  • (MismatchingLibrary.framework, nil, BUILT_PRODUCTS_DIR) → (nil, Products, <group>) → (nil, nil, <group>)
  • (NewFramework.framework, nil, BUILT_PRODUCTS_DIR) → (nil, Products, <group>) → (nil, nil, <group>)
  • (NewFramework.h, nil, <group>) → (NewFramework, nil, <group>) → (nil, nil, <group>)
  • (Project.xcconfig, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)
  • (README.md, nil, <group>) → (nil, nil, <group>)
  • (Target.xcconfig, nil, <group>) → (Project, nil, <group>) → (nil, nil, <group>)

✅ DEPENDENCIES > "MismatchingLibrary" target

❌ DEPENDENCIES > "Project" target

⚠️ Only in second (2):

  • (target=MismatchingLibrary)
  • (target=NewFramework)

✅ DEPENDENCIES > "ProjectFramework" target

✅ DEPENDENCIES > "ProjectTests" target

✅ DEPENDENCIES > "ProjectUITests" target

✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target

❌ LINKED_DEPENDENCIES > "Project" target

⚠️ Only in second (2):

  • MismatchingLibrary.framework
  • NewFramework.framework

⚠️ Value mismatch (1):

  • ARKit.framework attributes

    • required
    • optional

✅ LINKED_DEPENDENCIES > "ProjectFramework" target

✅ LINKED_DEPENDENCIES > "ProjectTests" target

✅ LINKED_DEPENDENCIES > "ProjectUITests" target

diff --git a/CommandTests/Generated/list.0.a330395c.md b/CommandTests/Generated/list.0.a330395c.md index 4b5d34e..5539acf 100644 --- a/CommandTests/Generated/list.0.a330395c.md +++ b/CommandTests/Generated/list.0.a330395c.md @@ -24,6 +24,7 @@ inclusion status when tags aren't explicitly specified. - SETTINGS | Yes - RESOLVED_SETTINGS | No - SOURCE_TREES | Yes +- DEPENDENCIES | Yes - LINKED_DEPENDENCIES | Yes ``` diff --git a/CommandTests/Generated/p1_p2.2.bac5a245.md b/CommandTests/Generated/p1_p2.2.bac5a245.md index 353d360..e5bd126 100644 --- a/CommandTests/Generated/p1_p2.2.bac5a245.md +++ b/CommandTests/Generated/p1_p2.2.bac5a245.md @@ -62,6 +62,11 @@ ✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration ✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values ❌ SOURCE_TREES > Root project +✅ DEPENDENCIES > "MismatchingLibrary" target +❌ DEPENDENCIES > "Project" target +✅ DEPENDENCIES > "ProjectFramework" target +✅ DEPENDENCIES > "ProjectTests" target +✅ DEPENDENCIES > "ProjectUITests" target ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target ❌ LINKED_DEPENDENCIES > "Project" target ✅ LINKED_DEPENDENCIES > "ProjectFramework" target diff --git a/CommandTests/Generated/p1_p2_Project_target.2.961f27cf.md b/CommandTests/Generated/p1_p2_Project_target.2.961f27cf.md index 5398043..899e55e 100644 --- a/CommandTests/Generated/p1_p2_Project_target.2.961f27cf.md +++ b/CommandTests/Generated/p1_p2_Project_target.2.961f27cf.md @@ -26,6 +26,7 @@ ✅ SETTINGS > "Project" target > "Release" configuration > Base configuration ❌ SETTINGS > "Project" target > "Release" configuration > Values ❌ SOURCE_TREES > Root project +❌ DEPENDENCIES > "Project" target ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/CommandTests/Generated/p1_p2_Project_target_console_format.2.dfbdb9cf.md b/CommandTests/Generated/p1_p2_Project_target_console_format.2.dfbdb9cf.md index e6d9349..537736f 100644 --- a/CommandTests/Generated/p1_p2_Project_target_console_format.2.dfbdb9cf.md +++ b/CommandTests/Generated/p1_p2_Project_target_console_format.2.dfbdb9cf.md @@ -26,6 +26,7 @@ ✅ SETTINGS > "Project" target > "Release" configuration > Base configuration ❌ SETTINGS > "Project" target > "Release" configuration > Values ❌ SOURCE_TREES > Root project +❌ DEPENDENCIES > "Project" target ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/CommandTests/Generated/p1_p2_Project_target_console_format_verbose.2.ba4ce842.md b/CommandTests/Generated/p1_p2_Project_target_console_format_verbose.2.ba4ce842.md index 8d7509b..c2deb38 100644 --- a/CommandTests/Generated/p1_p2_Project_target_console_format_verbose.2.ba4ce842.md +++ b/CommandTests/Generated/p1_p2_Project_target_console_format_verbose.2.ba4ce842.md @@ -177,6 +177,14 @@ Output format: (, , ) • (Target.xcconfig, nil, ) → (Project, nil, ) → (nil, nil, ) +❌ DEPENDENCIES > "Project" target + +⚠️ Only in second (2): + + • (target=MismatchingLibrary) + • (target=NewFramework) + + ❌ LINKED_DEPENDENCIES > "Project" target ⚠️ Only in second (2): diff --git a/CommandTests/Generated/p1_p2_Project_target_json_format.2.1b44ee6e.md b/CommandTests/Generated/p1_p2_Project_target_json_format.2.1b44ee6e.md index 695b11d..3c9ba61 100644 --- a/CommandTests/Generated/p1_p2_Project_target_json_format.2.1b44ee6e.md +++ b/CommandTests/Generated/p1_p2_Project_target_json_format.2.1b44ee6e.md @@ -357,6 +357,22 @@ ], "tag" : "source_trees" }, + { + "context" : [ + "\"Project\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + "(target=MismatchingLibrary)", + "(target=NewFramework)" + ], + "tag" : "dependencies" + }, { "context" : [ "\"Project\" target" diff --git a/CommandTests/Generated/p1_p2_Project_target_json_format_verbose.2.bcad53ce.md b/CommandTests/Generated/p1_p2_Project_target_json_format_verbose.2.bcad53ce.md index e3b120f..9d9d119 100644 --- a/CommandTests/Generated/p1_p2_Project_target_json_format_verbose.2.bcad53ce.md +++ b/CommandTests/Generated/p1_p2_Project_target_json_format_verbose.2.bcad53ce.md @@ -357,6 +357,22 @@ ], "tag" : "source_trees" }, + { + "context" : [ + "\"Project\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + "(target=MismatchingLibrary)", + "(target=NewFramework)" + ], + "tag" : "dependencies" + }, { "context" : [ "\"Project\" target" diff --git a/CommandTests/Generated/p1_p2_Project_target_markdown_format.2.d851dd25.md b/CommandTests/Generated/p1_p2_Project_target_markdown_format.2.d851dd25.md index ad910a3..35f509a 100644 --- a/CommandTests/Generated/p1_p2_Project_target_markdown_format.2.d851dd25.md +++ b/CommandTests/Generated/p1_p2_Project_target_markdown_format.2.d851dd25.md @@ -63,6 +63,9 @@ ## ❌ SOURCE_TREES > Root project +## ❌ DEPENDENCIES > "Project" target + + ## ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/CommandTests/Generated/p1_p2_Project_target_markdown_format_verbose.2.f582a13e.md b/CommandTests/Generated/p1_p2_Project_target_markdown_format_verbose.2.f582a13e.md index 0dfbc22..df03a62 100644 --- a/CommandTests/Generated/p1_p2_Project_target_markdown_format_verbose.2.f582a13e.md +++ b/CommandTests/Generated/p1_p2_Project_target_markdown_format_verbose.2.f582a13e.md @@ -214,6 +214,16 @@ Output format: (, , ) +## ❌ DEPENDENCIES > "Project" target + + +### ⚠️ Only in second (2): + + - `(target=MismatchingLibrary)` + - `(target=NewFramework)` + + + ## ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/CommandTests/Generated/p1_p2_Project_target_verbose.2.6f518e43.md b/CommandTests/Generated/p1_p2_Project_target_verbose.2.6f518e43.md index 832a953..0e9af79 100644 --- a/CommandTests/Generated/p1_p2_Project_target_verbose.2.6f518e43.md +++ b/CommandTests/Generated/p1_p2_Project_target_verbose.2.6f518e43.md @@ -177,6 +177,14 @@ Output format: (, , ) • (Target.xcconfig, nil, ) → (Project, nil, ) → (nil, nil, ) +❌ DEPENDENCIES > "Project" target + +⚠️ Only in second (2): + + • (target=MismatchingLibrary) + • (target=NewFramework) + + ❌ LINKED_DEPENDENCIES > "Project" target ⚠️ Only in second (2): diff --git a/CommandTests/Generated/p1_p2_console_format.2.9841a8d7.md b/CommandTests/Generated/p1_p2_console_format.2.9841a8d7.md index 2022bd6..4bcecdb 100644 --- a/CommandTests/Generated/p1_p2_console_format.2.9841a8d7.md +++ b/CommandTests/Generated/p1_p2_console_format.2.9841a8d7.md @@ -62,6 +62,11 @@ ✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration ✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values ❌ SOURCE_TREES > Root project +✅ DEPENDENCIES > "MismatchingLibrary" target +❌ DEPENDENCIES > "Project" target +✅ DEPENDENCIES > "ProjectFramework" target +✅ DEPENDENCIES > "ProjectTests" target +✅ DEPENDENCIES > "ProjectUITests" target ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target ❌ LINKED_DEPENDENCIES > "Project" target ✅ LINKED_DEPENDENCIES > "ProjectFramework" target diff --git a/CommandTests/Generated/p1_p2_console_format_verbose.2.40a241bd.md b/CommandTests/Generated/p1_p2_console_format_verbose.2.40a241bd.md index ccd060e..18a4383 100644 --- a/CommandTests/Generated/p1_p2_console_format_verbose.2.40a241bd.md +++ b/CommandTests/Generated/p1_p2_console_format_verbose.2.40a241bd.md @@ -371,6 +371,18 @@ Output format: (, , ) • (Target.xcconfig, nil, ) → (Project, nil, ) → (nil, nil, ) +✅ DEPENDENCIES > "MismatchingLibrary" target +❌ DEPENDENCIES > "Project" target + +⚠️ Only in second (2): + + • (target=MismatchingLibrary) + • (target=NewFramework) + + +✅ DEPENDENCIES > "ProjectFramework" target +✅ DEPENDENCIES > "ProjectTests" target +✅ DEPENDENCIES > "ProjectUITests" target ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/CommandTests/Generated/p1_p2_json_format.2.e54c06ce.md b/CommandTests/Generated/p1_p2_json_format.2.e54c06ce.md index 033feaa..b554b79 100644 --- a/CommandTests/Generated/p1_p2_json_format.2.e54c06ce.md +++ b/CommandTests/Generated/p1_p2_json_format.2.e54c06ce.md @@ -994,6 +994,82 @@ ], "onlyInSecond" : [ + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"Project\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + "(target=MismatchingLibrary)", + "(target=NewFramework)" + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"ProjectFramework\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"ProjectTests\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"ProjectUITests\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"MismatchingLibrary\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + ], "tag" : "linked_dependencies" }, diff --git a/CommandTests/Generated/p1_p2_json_format_verbose.2.0e0a3eb6.md b/CommandTests/Generated/p1_p2_json_format_verbose.2.0e0a3eb6.md index f7a6e37..f227d60 100644 --- a/CommandTests/Generated/p1_p2_json_format_verbose.2.0e0a3eb6.md +++ b/CommandTests/Generated/p1_p2_json_format_verbose.2.0e0a3eb6.md @@ -994,6 +994,82 @@ ], "onlyInSecond" : [ + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"Project\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + "(target=MismatchingLibrary)", + "(target=NewFramework)" + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"ProjectFramework\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"ProjectTests\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"ProjectUITests\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + + ], + "tag" : "dependencies" + }, + { + "context" : [ + "\"MismatchingLibrary\" target" + ], + "differentValues" : [ + + ], + "onlyInFirst" : [ + + ], + "onlyInSecond" : [ + ], "tag" : "linked_dependencies" }, diff --git a/CommandTests/Generated/p1_p2_markdown_format.2.1e09644b.md b/CommandTests/Generated/p1_p2_markdown_format.2.1e09644b.md index b8f9ba0..228afd5 100644 --- a/CommandTests/Generated/p1_p2_markdown_format.2.1e09644b.md +++ b/CommandTests/Generated/p1_p2_markdown_format.2.1e09644b.md @@ -171,6 +171,21 @@ ## ❌ SOURCE_TREES > Root project +## ✅ DEPENDENCIES > "MismatchingLibrary" target + + +## ❌ DEPENDENCIES > "Project" target + + +## ✅ DEPENDENCIES > "ProjectFramework" target + + +## ✅ DEPENDENCIES > "ProjectTests" target + + +## ✅ DEPENDENCIES > "ProjectUITests" target + + ## ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target diff --git a/CommandTests/Generated/p1_p2_markdown_format_verbose.2.ac528bab.md b/CommandTests/Generated/p1_p2_markdown_format_verbose.2.ac528bab.md index 7c6bd5f..4129036 100644 --- a/CommandTests/Generated/p1_p2_markdown_format_verbose.2.ac528bab.md +++ b/CommandTests/Generated/p1_p2_markdown_format_verbose.2.ac528bab.md @@ -480,6 +480,28 @@ Output format: (, , ) +## ✅ DEPENDENCIES > "MismatchingLibrary" target + + +## ❌ DEPENDENCIES > "Project" target + + +### ⚠️ Only in second (2): + + - `(target=MismatchingLibrary)` + - `(target=NewFramework)` + + + +## ✅ DEPENDENCIES > "ProjectFramework" target + + +## ✅ DEPENDENCIES > "ProjectTests" target + + +## ✅ DEPENDENCIES > "ProjectUITests" target + + ## ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target diff --git a/CommandTests/Generated/p1_p2_verbose.2.fe666557.md b/CommandTests/Generated/p1_p2_verbose.2.fe666557.md index 17451cc..20e1bf1 100644 --- a/CommandTests/Generated/p1_p2_verbose.2.fe666557.md +++ b/CommandTests/Generated/p1_p2_verbose.2.fe666557.md @@ -371,6 +371,18 @@ Output format: (, , ) • (Target.xcconfig, nil, ) → (Project, nil, ) → (nil, nil, ) +✅ DEPENDENCIES > "MismatchingLibrary" target +❌ DEPENDENCIES > "Project" target + +⚠️ Only in second (2): + + • (target=MismatchingLibrary) + • (target=NewFramework) + + +✅ DEPENDENCIES > "ProjectFramework" target +✅ DEPENDENCIES > "ProjectTests" target +✅ DEPENDENCIES > "ProjectUITests" target ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target ❌ LINKED_DEPENDENCIES > "Project" target diff --git a/Documentation/Comparators.md b/Documentation/Comparators.md index 9f91bc9..b728ce5 100644 --- a/Documentation/Comparators.md +++ b/Documentation/Comparators.md @@ -12,7 +12,11 @@ Compares build configurations i.e. Debug, Release. ### `copy_files` -Compares copy files build phases i.e. embed frameworks, extensions +Compares copy files build phases i.e. embed frameworks, extensions. + +### `dependencies` + +Compares dependencies build phases. ### `file_references` diff --git a/Sources/XCDiffCore/Comparator/DependenciesComparator.swift b/Sources/XCDiffCore/Comparator/DependenciesComparator.swift new file mode 100644 index 0000000..156b2ac --- /dev/null +++ b/Sources/XCDiffCore/Comparator/DependenciesComparator.swift @@ -0,0 +1,142 @@ +// +// Copyright 2020 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import PathKit +import XcodeProj + +final class DependenciesComparator: Comparator { + let tag = "dependencies" + + private let targetsHelper = TargetsHelper() + + func compare(_ first: ProjectDescriptor, + _ second: ProjectDescriptor, + parameters: ComparatorParameters) throws -> [CompareResult] { + return try targetsHelper + .commonTargets(first, second, parameters: parameters) + .flatMap { try compare($0, $1, + firstSourceRoot: first.sourceRoot, + secondSourceRoot: second.sourceRoot) } + } + + // MARK: - Private + + private func compare(_ first: PBXTarget, _ second: PBXTarget, + firstSourceRoot: Path, + secondSourceRoot: Path) throws -> [CompareResult] { + return results(context: ["\"\(first.name)\" target"], + first: stringDependencyDescriptors(from: first, sourceRoot: firstSourceRoot), + second: stringDependencyDescriptors(from: second, sourceRoot: secondSourceRoot)) + } + + private func stringDependencyDescriptors(from target: PBXTarget, sourceRoot: Path) -> Set { + return target.dependencies + .map(dependencyDescriptor) + .map { $0.description(sourceRoot: sourceRoot) } + .toSet() + } + + private func dependencyDescriptor(from dependency: PBXTargetDependency) -> DependencyDescriptor { + let name = dependency.name + let productName = dependency.product?.productName + if let target = dependency.target { + return .init(name: name, productName: productName, context: .target(target.name)) + } + if let proxy = dependency.targetProxy { + return .init(name: name, productName: productName, context: .proxy(proxy.proxyType, proxy.containerPortal)) + } + return .init(name: name, productName: productName, context: .none) + } +} + +private struct DependencyDescriptor { + enum Context { + case none + case target(String) + case proxy(PBXContainerItemProxy.ProxyType?, PBXContainerItemProxy.ContainerPortal) + + func description(sourceRoot: Path) -> String { + switch self { + case .none: + return "context=none" + case let .target(name): + return "target=\(name)" + case let .proxy(proxyType, containerPortal): + var result = [String]() + if let proxyTypeString = string(from: proxyType) { + result.append(proxyTypeString) + } + result.append(string(from: containerPortal, sourceRoot: sourceRoot)) + return result.joined(separator: ", ") + } + } + + private func string(from proxyType: PBXContainerItemProxy.ProxyType?) -> String? { + guard let proxyType = proxyType else { + return nil + } + switch proxyType { + case .nativeTarget: + return "proxy_type=native_target" + case .reference: + return "proxy_type=reference" + case .other: + return "proxy_type=other" + } + } + + private func string(from containerPortal: PBXContainerItemProxy.ContainerPortal, + sourceRoot: Path) -> String { + switch containerPortal { + case let .fileReference(fileReference): + let pathHelper = PathHelper() + let path = try? pathHelper.fullPath(from: fileReference, sourceRoot: sourceRoot) + return "proxy_file_reference(path=\(path ?? "nil"))" + case let .project(project): + return "proxy_project=\(project.name)" + case .unknownObject: + return "proxy_unknown_object" + } + } + } + + let name: String? + let productName: String? + let context: Context + + func description(sourceRoot: Path) -> String { + var result = [String]() + + // name + if case let .target(target) = context { + if let name = name, name != target { + result.append("name=\(name)") + } + } else if let name = name { + result.append("name=\(name)") + } + + // product name + if let productName = productName { + result.append("product_name=\(productName.description)") + } + + // context + result.append(context.description(sourceRoot: sourceRoot)) + return "(\(result.joined(separator: ", ")))" + } +} diff --git a/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift b/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift index 7b95a78..2f18a34 100644 --- a/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift +++ b/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift @@ -17,8 +17,8 @@ import Foundation final class LinkedDependenciesComparator: Comparator { - private typealias DependencyDescriptorPair = (first: DependencyDescriptor, - second: DependencyDescriptor) + private typealias DependencyDescriptorPair = (first: LinkedDependencyDescriptor, + second: LinkedDependencyDescriptor) private typealias EmbeddedFrameworksDescriptorPair = (first: EmbeddedFrameworksDescriptor, second: EmbeddedFrameworksDescriptor) private let targetsHelper = TargetsHelper() @@ -37,8 +37,8 @@ final class LinkedDependenciesComparator: Comparator { } private func createLinkedDependenciesResults(commonTarget: TargetPair) throws -> CompareResult { - let firstDependencies = try targetsHelper.dependencies(from: commonTarget.first) - let secondDependencies = try targetsHelper.dependencies(from: commonTarget.second) + let firstDependencies = try targetsHelper.linkedDependencies(from: commonTarget.first) + let secondDependencies = try targetsHelper.linkedDependencies(from: commonTarget.second) let firstPaths = Set(firstDependencies.compactMap { dependencyKey(dependency: $0) }) let secondPaths = Set(secondDependencies.compactMap { dependencyKey(dependency: $0) }) @@ -54,13 +54,13 @@ final class LinkedDependenciesComparator: Comparator { differentValues: differences) } - private func dependencyKey(dependency: DependencyDescriptor) -> String? { + private func dependencyKey(dependency: LinkedDependencyDescriptor) -> String? { if let key = dependency.name ?? dependency.path { return key } return nil } - private func commonDependencyDescriptorPairs(first: [DependencyDescriptor], - second: [DependencyDescriptor]) -> [DependencyDescriptorPair] { + private func commonDependencyDescriptorPairs(first: [LinkedDependencyDescriptor], + second: [LinkedDependencyDescriptor]) -> [DependencyDescriptorPair] { let firstDependencyDescriptorMap = dependencyPathMap(from: first) let secondDependencyDescriptorMap = dependencyPathMap(from: second) @@ -94,8 +94,8 @@ final class LinkedDependenciesComparator: Comparator { } } - private func dependencyPathMap(from dependencyDescriptors: [DependencyDescriptor]) - -> [String: DependencyDescriptor] { + private func dependencyPathMap(from dependencyDescriptors: [LinkedDependencyDescriptor]) + -> [String: LinkedDependencyDescriptor] { return Dictionary(dependencyDescriptors.compactMap { if let key = dependencyKey(dependency: $0) { return (key, $0) } return nil diff --git a/Sources/XCDiffCore/ComparatorType.swift b/Sources/XCDiffCore/ComparatorType.swift index d84d440..5db0dfe 100644 --- a/Sources/XCDiffCore/ComparatorType.swift +++ b/Sources/XCDiffCore/ComparatorType.swift @@ -28,6 +28,7 @@ public enum ComparatorType { case settings case resolvedSettings case sourceTrees + case dependencies case linkedDependecies case custom(Comparator) @@ -60,6 +61,8 @@ public enum ComparatorType { return ResolvedSettingsComparator(system: DefaultSystem()) case .sourceTrees: return SourceTreesComparator() + case .dependencies: + return DependenciesComparator() case .linkedDependecies: return LinkedDependenciesComparator() case let .custom(comparator): @@ -82,6 +85,7 @@ public extension Array where Element == ComparatorType { .settings, .resolvedSettings, .sourceTrees, + .dependencies, .linkedDependecies, ] } @@ -98,6 +102,7 @@ public extension Array where Element == ComparatorType { .configurations, .settings, .sourceTrees, + .dependencies, .linkedDependecies, ] } diff --git a/Sources/XCDiffCore/Library/TargetsHelper.swift b/Sources/XCDiffCore/Library/TargetsHelper.swift index b7cc672..c57b8c7 100644 --- a/Sources/XCDiffCore/Library/TargetsHelper.swift +++ b/Sources/XCDiffCore/Library/TargetsHelper.swift @@ -30,7 +30,7 @@ struct HeaderDescriptor: Hashable { let attributes: String? } -struct DependencyDescriptor: Hashable { +struct LinkedDependencyDescriptor: Hashable { let name: String? let path: String? let type: DependencyDescriptorType @@ -132,16 +132,16 @@ final class TargetsHelper { .map { $0.name }) } - func dependencies(from target: PBXTarget) throws -> [DependencyDescriptor] { - guard let dependencies = target.buildPhases.compactMap({ $0 as? PBXFrameworksBuildPhase }).first, - let dependencyFiles = dependencies.files else { + func linkedDependencies(from target: PBXTarget) throws -> [LinkedDependencyDescriptor] { + guard let linkedDependencies = target.buildPhases.compactMap({ $0 as? PBXFrameworksBuildPhase }).first, + let dependencyFiles = linkedDependencies.files else { return [] } return dependencyFiles.compactMap { if $0.file?.name != nil || $0.file?.path != nil { - return DependencyDescriptor(name: $0.file?.name, - path: $0.file?.path, - type: $0.settings == nil ? .required : .optional) + return LinkedDependencyDescriptor(name: $0.file?.name, + path: $0.file?.path, + type: $0.settings == nil ? .required : .optional) } return nil } diff --git a/Tests/XCDiffCommandTests/CommandsRunnerTests.swift b/Tests/XCDiffCommandTests/CommandsRunnerTests.swift index 13e9220..d419d47 100644 --- a/Tests/XCDiffCommandTests/CommandsRunnerTests.swift +++ b/Tests/XCDiffCommandTests/CommandsRunnerTests.swift @@ -101,6 +101,11 @@ final class CommandsRunnerTests: XCTestCase { ✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Base configuration ✅ SETTINGS > "ProjectUITests" target > "Release" configuration > Values ✅ SOURCE_TREES > Root project + ✅ DEPENDENCIES > "MismatchingLibrary" target + ✅ DEPENDENCIES > "Project" target + ✅ DEPENDENCIES > "ProjectFramework" target + ✅ DEPENDENCIES > "ProjectTests" target + ✅ DEPENDENCIES > "ProjectUITests" target ✅ LINKED_DEPENDENCIES > "MismatchingLibrary" target ✅ LINKED_DEPENDENCIES > "Project" target ✅ LINKED_DEPENDENCIES > "ProjectFramework" target diff --git a/Tests/XCDiffCoreTests/Comparator/ComparatorTypeTests.swift b/Tests/XCDiffCoreTests/Comparator/ComparatorTypeTests.swift index a237ae6..416344e 100644 --- a/Tests/XCDiffCoreTests/Comparator/ComparatorTypeTests.swift +++ b/Tests/XCDiffCoreTests/Comparator/ComparatorTypeTests.swift @@ -33,6 +33,7 @@ final class ComperatorTypeTests: XCTestCase { "settings", "resolved_settings", "source_trees", + "dependencies", "linked_dependencies", ]) } diff --git a/Tests/XCDiffCoreTests/Comparator/DependenciesComparatorTests.swift b/Tests/XCDiffCoreTests/Comparator/DependenciesComparatorTests.swift new file mode 100644 index 0000000..bc503a0 --- /dev/null +++ b/Tests/XCDiffCoreTests/Comparator/DependenciesComparatorTests.swift @@ -0,0 +1,331 @@ +import Foundation +@testable import XCDiffCore +import XCTest + +// swiftlint:disable:next type_body_length +final class DependenciesTests: XCTestCase { + private var subject: DependenciesComparator! + + override func setUp() { + super.setUp() + + subject = DependenciesComparator() + } + + func testTag() { + // When / Then + XCTAssertEqual(subject.tag, "dependencies") + } + + func testCompare_whenNoDependencies() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, noDifference(targets: ["T1", "T2"])) + } + + func testCompare_whenDependenciesOnlyInFirst() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: ["T2", "T3"]) + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], onlyInFirst: ["(target=T2)", "(target=T3)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + .init(tag: "dependencies", context: ["\"T3\" target"]), + ]) + } + + func testCompare_whenDependenciesOnlyInSecond() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: ["T2"]) + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: ["T2", "T3"]) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], onlyInSecond: ["(target=T3)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + .init(tag: "dependencies", context: ["\"T3\" target"]), + ]) + } + + func testCompare_whenDependeciesOnlyInFirstWithoutName() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: .target(.init(name: nil, targetName: "T2"))) + .make(target: "T1", dependOn: .target(.init(name: nil, targetName: "T3"))) + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: ["T2"]) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], onlyInFirst: ["(target=T3)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + .init(tag: "dependencies", context: ["\"T3\" target"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithoutName() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: .target(.init(name: nil, targetName: "T2"))) + .make(target: "T1", dependOn: .target(.init(name: nil, targetName: "T3"))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], onlyInSecond: ["(target=T2)", "(target=T3)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + .init(tag: "dependencies", context: ["\"T3\" target"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithCustomName() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: .target(.init(name: "NameT2", targetName: "T2"))) + .make(target: "T1", dependOn: .target(.init(name: "NameT3", targetName: "T3"))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], + onlyInSecond: ["(name=NameT2, target=T2)", "(name=NameT3, target=T3)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + .init(tag: "dependencies", context: ["\"T3\" target"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithoutTarget() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .addTarget(name: "T3") + .make(target: "T1", dependOn: .target(.init(name: "Z1", targetName: nil))) + .make(target: "T1", dependOn: .target(.init(name: "Z2", targetName: nil))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], + onlyInSecond: ["(name=Z1, context=none)", "(name=Z2, context=none)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + .init(tag: "dependencies", context: ["\"T3\" target"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithProxy() throws { + // Given + let other = project(name: "PX") + .addTarget(name: "X1") + .projectDescriptor() + let first = project(name: "P1") + .addTarget(name: "T1") + .addTarget(name: "T2") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .addTarget(name: "T2") + .make(target: "T1", dependOn: .targetProxy(.init(name: "X1", + proxyType: nil, + containerPortal: .project(other)))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", context: ["\"T1\" target"], onlyInSecond: ["(name=X1, proxy_project=PX)"]), + .init(tag: "dependencies", context: ["\"T2\" target"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithProxyNativeTargetType() throws { + // Given + let other = project(name: "PX") + .addTarget(name: "X1") + .projectDescriptor() + let first = project(name: "P1") + .addTarget(name: "T1") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .make(target: "T1", dependOn: .targetProxy(.init(name: "X1", + proxyType: .nativeTarget, + containerPortal: .project(other)))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", + context: ["\"T1\" target"], + onlyInSecond: ["(name=X1, proxy_type=native_target, proxy_project=PX)"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithProxyReferenceType() throws { + // Given + let other = project(name: "PX") + .addTarget(name: "X1") + .projectDescriptor() + let first = project(name: "P1") + .addTarget(name: "T1") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .make(target: "T1", dependOn: .targetProxy(.init(name: "X1", + proxyType: .reference, + containerPortal: .project(other)))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", + context: ["\"T1\" target"], + onlyInSecond: ["(name=X1, proxy_type=reference, proxy_project=PX)"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithProxyOtherType() throws { + // Given + let other = project(name: "PX") + .addTarget(name: "X1") + .projectDescriptor() + let first = project(name: "P1") + .addTarget(name: "T1") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .make(target: "T1", dependOn: .targetProxy(.init(name: "X1", + proxyType: .other, + containerPortal: .project(other)))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", + context: ["\"T1\" target"], + onlyInSecond: ["(name=X1, proxy_type=other, proxy_project=PX)"]), + ]) + } + + func testCompare_whenDependeciesOnlyInSecondWithFileReference() throws { + // Given + let first = project(name: "P1") + .addTarget(name: "T1") + .projectDescriptor() + let second = project(name: "P2") + .addTarget(name: "T1") + .make(target: "T1", dependOn: .targetProxy(.init(name: "X1", + proxyType: .reference, + containerPortal: .fileReference("filepath")))) + .projectDescriptor() + + // When + let actual = try subject.compare(first, second, parameters: .all) + + // Then + XCTAssertEqual(actual, [ + .init(tag: "dependencies", + context: ["\"T1\" target"], + onlyInSecond: ["(name=X1, proxy_type=reference, proxy_file_reference(path=filepath))"]), + ]) + } + + // MARK: - Helpers + + private func noDifference(targets: [String] = []) -> [CompareResult] { + targets.map { target in + .init(tag: "dependencies", context: ["\"\(target)\" target"]) + } + } +} diff --git a/Tests/XCDiffCoreTests/Helpers/PBXNativeTargetBuilder.swift b/Tests/XCDiffCoreTests/Helpers/PBXNativeTargetBuilder.swift index fee7aad..d569c7c 100644 --- a/Tests/XCDiffCoreTests/Helpers/PBXNativeTargetBuilder.swift +++ b/Tests/XCDiffCoreTests/Helpers/PBXNativeTargetBuilder.swift @@ -51,6 +51,7 @@ final class PBXNativeTargetBuilder { private var pbxtarget: PBXNativeTarget private var objects: [PBXObject] = [] private var fileElements: [PBXFileElement] = [] + private var dependencies: [PBXTargetDependency] = [] init(name: String, productType: PBXProductType?) { pbxtarget = PBXNativeTarget(name: name, productType: productType) @@ -205,6 +206,10 @@ final class PBXNativeTargetBuilder { } struct LinkedDependenciesData { + let name: String? + let path: String? + let settings: [String: [String]]? + init(name: String? = nil, path: String? = nil, settings: [String: [String]]? = nil) { @@ -212,10 +217,6 @@ struct LinkedDependenciesData { self.path = path self.settings = settings } - - let name: String? - let path: String? - let settings: [String: [String]]? } struct EmbeddedFrameworksData { diff --git a/Tests/XCDiffCoreTests/Helpers/PBXProjBuilder.swift b/Tests/XCDiffCoreTests/Helpers/PBXProjBuilder.swift index 6c2be75..304d631 100644 --- a/Tests/XCDiffCoreTests/Helpers/PBXProjBuilder.swift +++ b/Tests/XCDiffCoreTests/Helpers/PBXProjBuilder.swift @@ -20,6 +20,7 @@ import PathKit import XcodeProj import XCTest +// swiftlint:disable force_cast force_try final class PBXProjBuilder { private var pbxproj: PBXProj private var pbxproject: PBXProject @@ -98,12 +99,80 @@ final class PBXProjBuilder { return self } + @discardableResult + func make(target targetName: String, dependOn dependencyTargetNames: [String]) -> PBXProjBuilder { + let target = pbxproj.targets(named: targetName).first as! PBXNativeTarget + dependencyTargetNames.forEach { + let dependencyTarget = pbxproj.targets(named: $0).first! + _ = try! target.addDependency(target: dependencyTarget) + } + return self + } + + @discardableResult + func make(target targetName: String, dependOn dependency: DependencyType) -> PBXProjBuilder { + switch dependency { + case let .target(value): + return make(target: targetName, dependOn: value) + case let .targetProxy(value): + return make(target: targetName, dependOn: value) + case let .swiftPackage(value): + return make(target: targetName, dependOn: value) + } + } + func build() -> PBXProj { return pbxproj } // MARK: - Private + private func make(target targetName: String, dependOn dependency: TargetDependencyData) -> PBXProjBuilder { + let target = pbxproj.targets(named: targetName).first as! PBXNativeTarget + var dependencyTarget: PBXTarget? + if let targetName = dependency.targetName { + dependencyTarget = pbxproj.targets(named: targetName).first! + } + let targetDependency = PBXTargetDependency(name: dependency.name, target: dependencyTarget) + pbxproj.add(object: targetDependency) + target.dependencies.append(targetDependency) + return self + } + + private func make(target targetName: String, dependOn dependency: TargetProxyDependencyData) -> PBXProjBuilder { + let target = pbxproj.targets(named: targetName).first as! PBXNativeTarget + let containerPortal: PBXContainerItemProxy.ContainerPortal + switch dependency.containerPortal { + case let .project(descriptor): + containerPortal = .project(try! descriptor.pbxproj.rootProject()!) + case let .fileReference(path): + let fileReference = PBXFileReference(sourceTree: .absolute, path: path) + pbxproj.add(object: fileReference) + containerPortal = .fileReference(fileReference) + case .unknown: + containerPortal = .unknownObject(nil) + } + let targetProxy = PBXContainerItemProxy(containerPortal: containerPortal, proxyType: dependency.proxyType) + pbxproj.add(object: targetProxy) + let targetDependency = PBXTargetDependency(name: dependency.name, + targetProxy: targetProxy) + pbxproj.add(object: targetDependency) + target.dependencies.append(targetDependency) + return self + } + + private func make(target targetName: String, + dependOn dependency: SwiftPackageProductDependencyData) -> PBXProjBuilder { + let target = pbxproj.targets(named: targetName).first as! PBXNativeTarget + let swiftPackageDependency = XCSwiftPackageProductDependency(productName: dependency.productName) + pbxproj.add(object: swiftPackageDependency) + let targetDependency = PBXTargetDependency(name: dependency.name, + product: swiftPackageDependency) + pbxproj.add(object: targetDependency) + target.dependencies.append(targetDependency) + return self + } + private static func createNewProject(name: String, configurationListBuilder: XCConfigurationListBuilder, projectDirPath: String) -> (PBXProj, PBXProject) { @@ -126,6 +195,36 @@ final class PBXProjBuilder { } } +// swiftlint:enable force_cast force_try + +enum DependencyType { + case target(TargetDependencyData) + case targetProxy(TargetProxyDependencyData) + case swiftPackage(SwiftPackageProductDependencyData) +} + +struct TargetDependencyData { + let name: String? + let targetName: String? +} + +struct TargetProxyDependencyData { + let name: String? + let proxyType: PBXContainerItemProxy.ProxyType? + let containerPortal: ContainerPortal +} + +struct SwiftPackageProductDependencyData { + let name: String? + let productName: String +} + +enum ContainerPortal { + case project(ProjectDescriptor) + case fileReference(String) + case unknown +} + extension PBXProjBuilder { func projectDescriptor() -> ProjectDescriptor { let workspace = XCWorkspace(data: XCWorkspaceData(children: []))