@@ -11,138 +11,120 @@ import (
1111 "github.com/bitrise-io/go-xcode/plistutil"
1212 "github.com/bitrise-io/go-xcode/profileutil"
1313 "github.com/bitrise-io/go-xcode/v2/xcodeversion"
14- "github.com/bitrise-io/go-xcode/xcodeproject/serialized"
15- "github.com/bitrise-io/go-xcode/xcodeproject/xcodeproj"
16- "github.com/bitrise-io/go-xcode/xcodeproject/xcscheme"
1714)
1815
1916const (
2017 // AppClipProductType ...
2118 AppClipProductType = "com.apple.product-type.application.on-demand-install-capable"
2219)
2320
21+ // Opts contains options for the exportOptions generator.
22+ type Opts struct {
23+ ContainerEnvironment string
24+ TeamID string
25+ UploadBitcode bool
26+ CompileBitcode bool
27+ ArchivedWithXcodeManagedProfiles bool
28+ TestFlightInternalTestingOnly bool
29+ ManageVersionAndBuildNumber bool
30+ }
31+
2432// ExportOptionsGenerator generates an exportOptions.plist file.
2533type ExportOptionsGenerator struct {
26- xcodeProj * xcodeproj.XcodeProj
27- scheme * xcscheme.Scheme
28- configuration string
29-
3034 xcodeVersionReader xcodeversion.Reader
3135 certificateProvider CodesignIdentityProvider
3236 profileProvider ProvisioningProfileProvider
33- targetInfoProvider TargetInfoProvider
3437 logger log.Logger
3538}
3639
3740// New constructs a new ExportOptionsGenerator.
38- func New (xcodeProj * xcodeproj.XcodeProj , scheme * xcscheme.Scheme , configuration string , xcodeVersionReader xcodeversion.Reader , logger log.Logger ) ExportOptionsGenerator {
39- g := ExportOptionsGenerator {
40- xcodeProj : xcodeProj ,
41- scheme : scheme ,
42- configuration : configuration ,
43- xcodeVersionReader : xcodeVersionReader ,
44- }
45- g .certificateProvider = LocalCodesignIdentityProvider {}
46- g .profileProvider = LocalProvisioningProfileProvider {}
47- g .targetInfoProvider = XcodebuildTargetInfoProvider {xcodeProj : xcodeProj }
48- g .logger = logger
49- return g
41+ func New (xcodeVersionReader xcodeversion.Reader , logger log.Logger ) ExportOptionsGenerator {
42+ return ExportOptionsGenerator {
43+ xcodeVersionReader : xcodeVersionReader ,
44+ certificateProvider : LocalCodesignIdentityProvider {},
45+ profileProvider : LocalProvisioningProfileProvider {},
46+ logger : logger ,
47+ }
5048}
5149
5250// GenerateApplicationExportOptions generates exportOptions for an application export.
5351func (g ExportOptionsGenerator ) GenerateApplicationExportOptions (
52+ exportedProduct ExportProduct ,
53+ archiveInfo ArchiveInfo ,
5454 exportMethod exportoptions.Method ,
55- containerEnvironment string ,
56- teamID string ,
57- uploadBitcode bool ,
58- compileBitcode bool ,
59- archivedWithXcodeManagedProfiles bool ,
6055 codeSigningStyle exportoptions.SigningStyle ,
61- testFlightInternalTestingOnly bool ,
56+ opts Opts ,
6257) (exportoptions.ExportOptions , error ) {
6358 xcodeVersion , err := g .xcodeVersionReader .GetVersion ()
6459 if err != nil {
6560 return nil , fmt .Errorf ("failed to get Xcode version: %w" , err )
6661 }
6762
68- mainTargetBundleID , entitlementsByBundleID , err := g .applicationTargetsAndEntitlements (exportMethod )
69- if err != nil {
70- return nil , err
63+ // BundleIDs appear in the export options plist in:
64+ // - distributionBundleIdentifier: can be the main app or the app Clip bundle ID.
65+ // It is only valid for NON app-store-connect distribution. App Store export includes both app and app-clip in one go, others do not.
66+ // - provisioningProfiles dictionary:
67+ // When distributing an app-clip, its bundle ID needs to be in the provisioningProfiles dictionary, otherwise it needs to be removed.
68+ productToDistributeBundleID := archiveInfo .AppBundleID
69+ if exportedProduct == ExportProductAppClip {
70+ if archiveInfo .AppClipBundleID == "" {
71+ return nil , fmt .Errorf ("xcarchive does not contain an App Clip, cannot export an App Clip" )
72+ }
73+
74+ if exportMethod .IsAppStore () {
75+ g .logger .Warnf ("Selected app-clip for distribution, but distribution method is the App Store.\n " +
76+ "Exported .app will contain both the app and the app-clip for App Store exports.\n " )
77+ }
78+ productToDistributeBundleID = archiveInfo .AppClipBundleID
7179 }
7280
73- iCloudContainerEnvironment , err := determineIcloudContainerEnvironment (containerEnvironment , entitlementsByBundleID , exportMethod , xcodeVersion .Major )
81+ if exportedProduct != ExportProductAppClip {
82+ for bundleID := range archiveInfo .EntitlementsByBundleID {
83+ if bundleID == archiveInfo .AppClipBundleID && ! exportMethod .IsAppStore () {
84+ g .logger .Debugf ("Filtering out App Clip target, as non App Store distribution is used: %s" , bundleID )
85+ delete (archiveInfo .EntitlementsByBundleID , bundleID )
86+ }
87+ }
88+ }
89+
90+ iCloudContainerEnvironment , err := determineIcloudContainerEnvironment (opts .ContainerEnvironment , archiveInfo .EntitlementsByBundleID , exportMethod , xcodeVersion .Major )
7491 if err != nil {
7592 return nil , err
7693 }
7794
78- exportOpts := generateBaseExportOptions (exportMethod , xcodeVersion , uploadBitcode , compileBitcode , iCloudContainerEnvironment )
95+ exportOpts := generateBaseExportOptions (exportMethod , xcodeVersion , opts . UploadBitcode , opts . CompileBitcode , iCloudContainerEnvironment )
7996
8097 if xcodeVersion .Major >= 12 {
81- exportOpts = addDistributionBundleIdentifierFromXcode12 (exportOpts , mainTargetBundleID )
98+ exportOpts = addDistributionBundleIdentifierFromXcode12 (exportOpts , productToDistributeBundleID )
8299 }
83100
84101 if xcodeVersion .Major >= 13 {
85- exportOpts = disableManagedBuildNumberFromXcode13 (exportOpts )
102+ exportOpts = addManagedBuildNumberFromXcode13 (exportOpts , opts . ManageVersionAndBuildNumber )
86103 }
87104
88105 if codeSigningStyle == exportoptions .SigningStyleAutomatic {
89- exportOpts = addTeamID (exportOpts , teamID )
106+ exportOpts = addTeamID (exportOpts , opts . TeamID )
90107 } else {
91- codeSignGroup , err := g .determineCodesignGroup (entitlementsByBundleID , exportMethod , teamID , archivedWithXcodeManagedProfiles )
108+ codeSignGroup , err := g .determineCodesignGroup (archiveInfo . EntitlementsByBundleID , exportMethod , opts . TeamID , opts . ArchivedWithXcodeManagedProfiles )
92109 if err != nil {
93110 return nil , err
94111 }
95112 if codeSignGroup == nil {
96113 return exportOpts , nil
97114 }
98115
99- exportOpts = addManualSigningFields (exportOpts , codeSignGroup , archivedWithXcodeManagedProfiles , g .logger )
116+ exportOpts = addManualSigningFields (exportOpts , codeSignGroup , opts . ArchivedWithXcodeManagedProfiles , g .logger )
100117 }
101118
102119 if xcodeVersion .Major >= 15 {
103- if testFlightInternalTestingOnly {
104- exportOpts = addTestFlightInternalTestingOnly (exportOpts , testFlightInternalTestingOnly )
120+ if opts . TestFlightInternalTestingOnly {
121+ exportOpts = addTestFlightInternalTestingOnly (exportOpts , opts . TestFlightInternalTestingOnly )
105122 }
106123 }
107124
108125 return exportOpts , nil
109126}
110127
111- func (g ExportOptionsGenerator ) applicationTargetsAndEntitlements (exportMethod exportoptions.Method ) (string , map [string ]plistutil.PlistData , error ) {
112- mainTarget , err := ArchivableApplicationTarget (g .xcodeProj , g .scheme )
113- if err != nil {
114- return "" , nil , err
115- }
116-
117- dependentTargets := filterApplicationBundleTargets (
118- g .xcodeProj .DependentTargetsOfTarget (* mainTarget ),
119- exportMethod ,
120- )
121- targets := append ([]xcodeproj.Target {* mainTarget }, dependentTargets ... )
122-
123- var mainTargetBundleID string
124- entitlementsByBundleID := map [string ]plistutil.PlistData {}
125- for i , target := range targets {
126- bundleID , err := g .targetInfoProvider .TargetBundleID (target .Name , g .configuration )
127- if err != nil {
128- return "" , nil , fmt .Errorf ("failed to get target (%s) bundle id: %s" , target .Name , err )
129- }
130-
131- entitlements , err := g .targetInfoProvider .TargetCodeSignEntitlements (target .Name , g .configuration )
132- if err != nil && ! serialized .IsKeyNotFoundError (err ) {
133- return "" , nil , fmt .Errorf ("failed to get target (%s) bundle id: %s" , target .Name , err )
134- }
135-
136- entitlementsByBundleID [bundleID ] = plistutil .PlistData (entitlements )
137-
138- if i == 0 {
139- mainTargetBundleID = bundleID
140- }
141- }
142-
143- return mainTargetBundleID , entitlementsByBundleID , nil
144- }
145-
146128// determineCodesignGroup finds the best codesign group (certificate + profiles)
147129// based on the installed Provisioning Profiles and Codesign Certificates.
148130func (g ExportOptionsGenerator ) determineCodesignGroup (bundleIDEntitlementsMap map [string ]plistutil.PlistData , exportMethod exportoptions.Method , teamID string , xcodeManaged bool ) (* export.IosCodeSignGroup , error ) {
@@ -376,10 +358,10 @@ func addDistributionBundleIdentifierFromXcode12(exportOpts exportoptions.ExportO
376358 return nil
377359}
378360
379- func disableManagedBuildNumberFromXcode13 (exportOpts exportoptions.ExportOptions ) exportoptions.ExportOptions {
361+ func addManagedBuildNumberFromXcode13 (exportOpts exportoptions.ExportOptions , isManageAppVersion bool ) exportoptions.ExportOptions {
380362 switch options := exportOpts .(type ) {
381363 case exportoptions.AppStoreOptionsModel :
382- options .ManageAppVersion = false // Only available for app-store exports
364+ options .ManageAppVersion = isManageAppVersion // Only available for app-store exports
383365
384366 return options
385367 }
0 commit comments