Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET: What to do about files in ResolvedFileToPublish. #12572

Closed
rolfbjarne opened this issue Aug 27, 2021 · 11 comments · Fixed by #13591
Closed

.NET: What to do about files in ResolvedFileToPublish. #12572

rolfbjarne opened this issue Aug 27, 2021 · 11 comments · Fixed by #13591
Assignees
Labels
bug If an issue is a bug or a pull request a bug fix dotnet An issue or pull request related to .NET (6) dotnet-pri0 .NET 6: required for stable release request-for-comments The issue is a suggested idea seeking feedback
Projects
Milestone

Comments

@rolfbjarne
Copy link
Member

rolfbjarne commented Aug 27, 2021

During the build, all files that should be published are put into the @(ResolvedFileToPublish) item group, and at the end of the build process, .NET will publish all the files in that item group. This is out of our (xamarin-macios') control, and just how the build process work in .NET.

Note: In this particular scenario we define "publish" as "create an app bundle that can be executed" (on Apple platforms, an "app (bundle)" is just a directory with a predefined structure).

So in order to properly create an app for the platforms we support, we currently adjust the directory where files should go (by changing the RelativePath metadata).

This is mostly done here:

<!-- include .dll, .exe and, for debugging only, .pdb files inside the .app -->
<ResolvedFileToPublish
Update="@(ResolvedFileToPublish)"
RelativePath="$(_AssemblyPublishDir)\%(ResolvedFileToPublish.DestinationSubDirectory)\%(Filename)%(Extension)"
Condition="'%(Extension)' == '.dll' Or ('$(_BundlerDebug)' == 'true' And '%(Extension)' == '.pdb') Or '%(Extension)' == '.exe'" />
<!-- Copy the app.config file to the app bundle -->
<ResolvedFileToPublish
Update="@(ResolvedFileToPublish)"
RelativePath="$(_AssemblyPublishDir)\%(ResolvedFileToPublish.DestinationSubDirectory)\%(ResolvedFileToPublish.TargetPath)"
Condition="'$(AppConfig)' != '' And '%(ResolvedFileToPublish.OriginalItemSpec)' == '$(AppConfig)' And '%(ResolvedFileToPublish.Link)' == 'app.config' And '%(ResolvedFileToPublish.TargetPath)' != ''" />
<!-- .dylib are never needed (nor allowed) for fully AOT'ed applications FIXME https://github.com/xamarin/xamarin-macios/issues/11145 -->
<ResolvedFileToPublish
Update="@(ResolvedFileToPublish)"
RelativePath="$(_DylibPublishDir)\%(Filename)%(Extension)"
Condition="('$(_SdkIsSimulator)' != 'false' Or '$(_PlatformName)' == 'macOS' Or '$(_PlatformName)' == 'MacCatalyst') And '%(Extension)' == '.dylib'" />
<ResolvedFileToPublish
Update="@(ResolvedFileToPublish)"
RelativePath="$(_AssemblyPublishDir)\%(Filename)%(Extension)"
Condition="'$(_PlatformName)' != 'macOS' And '$(InvariantGlobalization)' != 'true' And '%(Filename)%(Extension)' == '$(_GlobalizationDataFile)'" />

And here we also remove files that shouldn't be published (but are needed at some point during the build):

<!-- Remove the libxamarin-*.dylib files we don't want -->
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'%(Extension)' == '.dylib' And '%(Filename)%(Extension)' != '$(_LibXamarinName)' And $([System.String]::new('%(Filename)').StartsWith('libxamarin-dotnet', StringComparison.Ordinal))" />
<!-- Remove the runtime dylibs if we're linking the runtime statically -->
<ResolvedFileToPublish
Remove="@(ResolvedFileToPublish)"
Condition=" '$(_LibMonoExtension)' != 'dylib' And
'%(ResolvedFileToPublish.AssetType)' == 'native' And
'%(ResolvedFileToPublish.RuntimeIdentifier)' == '$(RuntimeIdentifier)' And
'%(ResolvedFileToPublish.Extension)' == '.dylib' And
'%(ResolvedFileToPublish.NuGetPackageId)' == '$(_MonoNugetPackageId)'
"
/>
<!-- There's no need to ship .a files -->
<ResolvedFileToPublish
Remove="@(ResolvedFileToPublish)"
Condition=" '%(ResolvedFileToPublish.AssetType)' == 'native' And
'%(ResolvedFileToPublish.RuntimeIdentifier)' == '$(RuntimeIdentifier)' And
'%(ResolvedFileToPublish.Extension)' == '.a' And
'%(ResolvedFileToPublish.NuGetPackageId)' == '$(_MonoNugetPackageId)'
"
/>
<!-- Put the 'createdump' executable in the expected location in the app bundle when using CoreCLR -->
<!-- Ref: https://github.com/xamarin/xamarin-macios/issues/11432 -->
<ResolvedFileToPublish
Update="@(ResolvedFileToPublish)"
RelativePath="$(_DylibPublishDir)\%(Filename)%(Extension)"
Condition=" '$(UseMonoRuntime)' == 'false' And
'%(ResolvedFileToPublish.Filename)' == 'createdump' And
'%(ResolvedFileToPublish.Extension)' == '' And
'%(ResolvedFileToPublish.AssetType)' == 'native' And
'%(ResolvedFileToPublish.RuntimeIdentifier)' == '$(RuntimeIdentifier)' And
'%(ResolvedFileToPublish.NuGetPackageId)' == '$(_MonoNugetPackageId)'
"
/>
<!-- Remove any dylibs Mono told us not to link with -->
<ResolvedFileToPublish
Remove="@(_MonoRuntimeComponentDontLink -> '$(_MonoRuntimePackPath)/native/%(Identity)')"
Condition=" '%(ResolvedFileToPublish.AssetType)' == 'native' And
'%(ResolvedFileToPublish.RuntimeIdentifier)' == '$(RuntimeIdentifier)' And
'%(ResolvedFileToPublish.NuGetPackageId)' == '$(_MonoNugetPackageId)'
"

These are handling cases for files that we publish, which is what we've mostly run into so far.

However, there are numerous sources for the files in ResolvedFileToPublish, and it's not clear what to do in each case. The problem is that there is no single publish directory for our platforms, different files should be placed in different subfolders within the app bundle (and it varies by platform too), so this isn't a trivial question to answer [1].

As far as I've been able to figure out, files in @(ResolvedFileToPublish) can come from a few sources:

  • Runtime packs (our own, or the runtime itself (CoreCLR/MonoVM)). We have some logic to detect this (see the links above).
  • @(None), @(Content) and @(EmbeddedResource) items with the CopyToOutputDirectory or the CopyToPublishDirectory metadata set. It goes like this:
    • These items are assigned a TargetPath metadata in the AssignTargetPaths target
    • The GetCopyToPublishDirectoryItems target then filters on 'CopyToPublishDirectory' and assigns to @(_SourceItemsToCopyToPublishDirectory).
    • The _ComputeCopyToPublishDirectoryItems target adds @(_SourceItemsToCopyToPublishDirectory) to @(ResolvedFileToPublish)
  • The output from referenced projects (transitively).
  • NuGets with a runtimes/RID/native directory. An aggravating issue here is that NuGet will strip the relative path at some point (dotnet publish with a rid flattens Nuget package files.  dotnet/sdk#9643).
    • The ResolvePackageAssets adds to the NativeCopyLocalItems group
    • Then ResolveLockFileCopyLocalFiles adds it to ReferenceCopyLocalPaths
  • And of course someone will eventually add files directly to $(ResolvedFileToPublish) themselves.

For legacy Xamarin we solved this by saying that if a developer wanted a file copied to the app, then they had to add it to the @(BundleResource) item group, which allows you to specify the directory within the app where the file should be located. This still works in .NET, but it's a unique item group to our platforms, and doesn't play very well the ecosystem (see the list of related issues below for a few examples).

I propose that we do an educated guess on where files should be placed, depending on the filetype, and with a way for developers to override the default (I'm only talking about files where we believe the file was added to the project by the user somehow, either by adding it directly to the project file, or a third-party NuGet, etc. It specifically does not cover files we ship, nor that are shipped with the CoreCLR/MonoVM runtime packs).

  1. We guess based on file type:
    • Assemblies and their related files (*.dll, *.exe, *.pdb, *.mdb, *.config) are copied to the directories where assemblies are located in the app bundle (root directory for iOS and tvOS, /Contents/MonoBundle for macOS and Mac Catalyst).
    • Native frameworks (*.framework/*) are copied to the frameworks directory (/Frameworks for iOS and tvOS, /Contents/Frameworks for macOS and Mac Catalyst).
    • Native xc frameworks (.xcframework/*) are treated as *.framework/*, with some custom logic to filter out files that are for other platforms.
    • *.resources/* files next to an assembly with the same name is treated as a third-party binding, and we handle it as such (the exact details are not relevant for this discussion).
    • Resources (*.jpg, *.png, ...?) are copied to the resources directory (/Resources for iOS and tvOS, /Contents/Resources for macOS and Mac Catalyst).
    • *.framework.zip and *.xcframework.zip are treated as a zipped (xc)framework: unzipped, and then handled like the non-compressed version.
    • Anything else?
    • No other files are copied. We show a warning if we find any such files.
      • I believe this is the best way to leave us open to add support for more file types in the future. Once we decide a location for a particular file type, we won't be able to suddenly put it somewhere else later.
        In all cases the relative directory is preserved (i.e. we figure out a way around dotnet publish with a rid flattens Nuget package files.  dotnet/sdk#9643).
  2. Developers can override the target location by:
    • Setting the Link metadata on items. This seems to follow how it's already done: Allow CopyToOutputDirectory to have a custom destination path dotnet/msbuild#2795 (comment).
    • If Link isn't appropriate (it also controls how the file shows up in the solution explorer in the IDE, and who knows what else too), then the developer can set the AppBundleLocation metadata on files to overrideLink. This is relative to the root directory of the app bundle, and the developer will have to take into account that the correct directory is going to be different between mobile platforms (iOS and tvOS) and desktop platforms (macOS and Mac Catalyst).
    • Setting CopyToOutputDirectory=Never on items that shouldn't be copied.

Related issues

dotnet/msbuild#6237
dotnet/sdk#9643
#12369
#12386
#12418
#12440
#11667
#12174

CC @jonathanpeppers @filipnavara @Redth @spouliot @dalexsoto @KirillOsenkov @mhutch @dsplaisted (feel free to CC anybody else that might be relevant)

[1]: In particular for macOS and Mac Catalyst, putting anything the root directory in the app bundle is always the wrong choice, because such app bundles are not valid app bundles, and it's not possible to sign them (the codesign tool will just fail).

@rolfbjarne rolfbjarne added request-for-comments The issue is a suggested idea seeking feedback dotnet An issue or pull request related to .NET (6) dotnet-pri0 .NET 6: required for stable release labels Aug 27, 2021
@rolfbjarne rolfbjarne added this to the .NET 6 milestone Aug 27, 2021
@rolfbjarne rolfbjarne added this to Needs investigation/decision in .NET 6 Aug 27, 2021
@rolfbjarne rolfbjarne self-assigned this Aug 27, 2021
@rolfbjarne rolfbjarne added the bug If an issue is a bug or a pull request a bug fix label Aug 27, 2021
@dalexsoto
Copy link
Member

Option 1 has my vote, this way we are in control of the known filetypes and we can warn on the rest so there is no magic behavior and maybe we can point people to use BundleResource if they want to include the warned items.

@SteveBush
Copy link

SteveBush commented Sep 2, 2021

I think it's a common scenario for solutions containing a MAUI application, libraries and unit tests to publish to a common repo root bin and obj folders. I have not figured out a way to make this work when building IOS and MacCatalyst apps. Works fine for Android.

I'm developing a MAUI application that includes a shared library, MAUI app (IOS, MacCatalyst, Android), and a set of unit tests. I have modified the all of projects to use TargetFrameworks so I can have different targeted frameworks for the library, unit tests, and MAUI App.

Everything works great except the IOS and MacCatalyst builds which cannot handle a fully qualified path for BaseOutputPath. If I comment out the following lines in the repo root Directory.Build.props, it builds fine locally. However, the bin and obj directories are now per project. Having per project bin and obj folders makes it more difficult for Azure and GitHub build pipelines to handle tasks like publishing code coverage and artifacts.

Directory.Build.props at solution root

    <RepoRootPath>$(MSBuildThisFileDirectory)</RepoRootPath>
    <BaseIntermediateOutputPath>$(RepoRootPath)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
    <BaseOutputPath Condition=" '$(BaseOutputPath)' == '' ">$(RepoRootPath)bin\$(MSBuildProjectName)\</BaseOutputPath>
    <PackageOutputPath>$(RepoRootPath)bin\Packages\$(Configuration)\</PackageOutputPath>

When I build, I get the following error:

Error	MSB4184	The expression "[MSBuild]::MakeRelative(D:\GitHub\NetworkVisor\Build.Test\src\NetworkVisor.Core.Devices.Test\D:\GitHub\NetworkVisor\Build.Test\bin\NetworkVisor.Core.Devices.Test\Debug\net6.0-maccatalyst\maccatalyst-x64\publish\, D:\GitHub\NetworkVisor\Build.Test\src\NetworkVisor.Core.Devices.Test\..\..\bin\NetworkVisor.Core.Devices.Test\Debug\net6.0-maccatalyst\maccatalyst-x64\NetworkVisor.Core.Devices.Test.app\\Contents\MonoBundle\)" cannot be evaluated. The given path's format is not supported.	NetworkVisor.Core.Devices.Test	C:\Program Files\dotnet\packs\Microsoft.MacCatalyst.Sdk\15.0.100-preview.7.230\targets\Xamarin.Shared.Sdk.targets	1	

I believe the offending code is in C:\Program Files\dotnet\packs\Microsoft.MacCatalyst.Sdk\15.0.100-preview.7.230\targets\Xamarin.Shared.Sdk.targets:

The calls to $([MSBuild]::MakeRelative($(MSBuildProjectDirectory)$(PublishDir) assumes $(PublishDir) is a relative path (bin). I cannot figure out how to get around this assumption and support IOS and MacCatalyst builds with root level obj and bin folders.

	<!-- Look in the NativeReference items for frameworks that need to be added to the app bundle, and add all those frameworks to ResolvedFileToPublish (as separate files) -->
	<Target Name="_ComputeFrameworkFilesToPublish" DependsOnTargets="_ExpandNativeReferences;_ComputeVariables">
		<ItemGroup>
			<!-- Expand each framework (which are directories) into all the files in the framework -->
			<!-- Support a 'CopyToAppBundle' metadata that can be set to 'false' to avoid copying a framework to the app bundle -->
			<_FrameworkFilesToPublish Include="%(_FrameworkNativeReference.RootDir)%(_FrameworkNativeReference.Directory)/**/*" Condition="'%(_FrameworkNativeReference.Kind)' == 'Framework' And '%(_FrameworkNativeReference.CopyToAppBundle)' != 'false'">
				<_FrameworkIdentity>%(RootDir)%(Directory)</_FrameworkIdentity>
				<_FrameworkPath>$([MSBuild]::MakeRelative($(MSBuildProjectDirectory)\$(PublishDir),$(_AppBundleFrameworksDir)))\%(Filename).framework</_FrameworkPath>
				<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
			</_FrameworkFilesToPublish>

			<!-- Compute the relative path of each file in the framework relative to the framework directory -->
			<_FrameworkFilesToPublish Update="@(_FrameworkFilesToPublish)">
				<_FrameworkRelativePath>$([System.String]::Copy('%(Identity)').Substring($([System.String]::Copy('%(_FrameworkIdentity)').Length)))</_FrameworkRelativePath>
			</_FrameworkFilesToPublish>

			<!-- Add all the framework files to ResolvedFileToPublish -->
			<ResolvedFileToPublish Include="@(_FrameworkFilesToPublish)">
				<RelativePath>%(_FrameworkFilesToPublish._FrameworkPath)\%(_FrameworkFilesToPublish._FrameworkRelativePath)</RelativePath>
				<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
			</ResolvedFileToPublish>
		</ItemGroup>
	</Target>

I'm adding this to the discussion as it's an example of how MacCatalyst and IOS builds handle OutputDir differently from Android and other target frameworks.

@dsplaisted
Copy link

@rolfbjarne It sounds like for iOS there are several different subfolders in the publish directory for different "publishing artifacts"? Can you describe the different subfolders and what they're used for?

Instead of modifying the RelativePath / Link metadata, I'm wondering if we should think about having a new type of metadata that specifies a subfolder / publish asset type.

@rolfbjarne
Copy link
Member Author

@SteveBush the problem with an absolute path for BaseOutputPath was a bug (#12224), and the fix will be included in RC 2.

@rolfbjarne
Copy link
Member Author

@dsplaisted

@rolfbjarne It sounds like for iOS there are several different subfolders in the publish directory for different "publishing artifacts"?

Correct. The complete output is a directory with the .app extension, and then a specific hierarchy inside (which depends on the platform).

Can you describe the different subfolders and what they're used for?

iOS and tvOS basically place everything in the root .app directory (with a few notable exceptions):

MyMobileApplication.app/
    MyImage.png <-- resources go into the root directory
    MyMobileApplication <-- this is the native executable
    MyMobileApplication.dll <-- the app's managed entry point
    System.Private.CoreLib.dll <-- more assemblies, and any related files (.pdb, .config, etc)...
    Frameworks/ <-- A directory of user frameworks (think dynamic libraries - they're very similar, just packaged differently)
        MyThirdPartyFramework.framework/ <-- this third party framework might come from a NuGet for instance
            MyThirdPartyFramework <-- the native code for the user framework
            Info.plist
    PlugIns/ <-- this directory will contain plugins/extension for the app, but we'll typically handle this for the developer (extensions aren't provided as-is from the developer to be included in the app bundle, they're produced as part of the build, and as such we have more knowledge about them)

macOS and Mac Catalyst use a few more subdirectories:

MyDesktopApplication.app/
    Contents/
        MacOS/
            MyDesktopApplication <-- this is the native executable. This directory might contain other native executables (such as helper tools or console executables), but this is rare.
        Frameworks/
            MyThirdPartyFramework.framework/ <-- this third party framework might come from a NuGet for instance
                    MyThirdPartyFramework <-- the native code for the user framework
                Resources/
                    Info.plist
        MonoBundle/ <-- we control this name, and it's also configurable by the developer
            MyDesktopApplication.dll <-- the app's managed entry point
            System.Private.CoreLib.dll <-- more assemblies, and any related files (.pdb, .config, etc)...
            MyThirdPartyDynamicLibrary.dll <-- dynamic libraries also usually go here (although they technically don't have to)
        Resources/
            MyImage.png <-- resources go into the Contents/Resources subdirectory
        PlugIns/ <-- this directory will contain plugins/extension for the app, but we'll typically handle this for the developer (extensions aren't provided as-is from the developer to be included in the app bundle, they're produced as part of the build, and as such we have more knowledge about them)

There is more information about the app bundle structure here: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1

Instead of modifying the RelativePath / Link metadata, I'm wondering if we should think about having a new type of metadata that specifies a subfolder / publish asset type.

I guess that could also work.

@radical
Copy link
Contributor

radical commented Sep 9, 2021

Instead of modifying the RelativePath / Link metadata, I'm wondering if we should think about having a new type of metadata that specifies a subfolder / publish asset type.

That would be useful for wasm too. For example - we want to differentiate .dll files like assembly references, from .dll files copied via, say, @(Content)+CopyToOutputdirectory.

@dalexsoto
Copy link
Member

@dsplaisted ping as requested!

@steveisok
Copy link
Contributor

I prefer option 1. I see this useful for at least all mobile configurations.

@dalexsoto
Copy link
Member

@dsplaisted just a friendly ping here!

@dsplaisted
Copy link

I think we should probably combine options 1 and 2. IE we should guess which publish directory files should go into, but that can be overridden by the app author.

So a multitargeted app could have something like this:

<Content Include="MyImage.png" />

And it would go into the right folder both on iOS and macOS, rather than having to conditionally set the Link metadata based on the TargetFramework or something. (Although normally the file probably wouldn't even be listed explicitly, it would come in through the globs.)

I am also leaning towards having the mechanism for overriding the directory be a separate, new piece of metadata. That way you could still use existing mechanisms (Link or RelativePath, I think) to put it in a subdirectory. So changing the publish directory of an item that was globbed by default would look something like this:

<Content Update="MyImage.png" PublishFolderType="Plugins"/>

That would mean having a list of valid values for PublishFolderType, and mapping them to the right folder for each platform.

@rolfbjarne
Copy link
Member Author

rolfbjarne commented Sep 22, 2021

RelativePath is already set for some items:

https://github.com/dotnet/sdk/blob/f61041364656df7823d1f2f494e810dee2b3b14f/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets#L374-L422

and we have to update that value because it's not correct for us - so the question becomes when do we update the value (because the value set by .NET is wrong), and when do we honor it (because the customer set it).

I guess the ideal option would be to set PublishFolderType for these items, so that RelativePath is always correct, but that seems a bit far-fetched to get into .NET 6 at this point.

Anyways, my updated suggestion would be to implement the following (both 1 and 2 combined, they're not mutually exclusive):

  1. We guess based on file type:
    • Assemblies and their related files (*.dll, *.exe, *.pdb, *.mdb, *.config) are copied to the directories where assemblies are located in the app bundle (root directory for iOS and tvOS, /Contents/MonoBundle for macOS and Mac Catalyst).
    • Native frameworks (*.framework/*) are copied to the frameworks directory (/Frameworks for iOS and tvOS, /Contents/Frameworks for macOS and Mac Catalyst).
    • Native xc frameworks (.xcframework/*) are treated as *.framework/*, with some custom logic to filter out files that are for other platforms.
    • *.resources/* files next to an assembly with the same name is treated as a third-party binding, and we handle it as such (the exact details are not relevant for this discussion).
    • Resources (*.jpg, *.png, ...?) are copied to the resources directory (/Resources for iOS and tvOS, /Contents/Resources for macOS and Mac Catalyst).
    • *.framework.zip and *.xcframework.zip are treated as a zipped (xc)framework: unzipped, and then handled like the non-compressed version.
    • Anything else?
    • No other files are copied. We show a warning if we find any such files.
      • I believe this is the best way to leave us open to add support for more file types in the future. Once we decide a location for a particular file type, we won't be able to suddenly put it somewhere else later.
        In all cases the relative directory is preserved (i.e. we figure out a way around dotnet publish with a rid flattens Nuget package files.  dotnet/sdk#9643).
  2. If we guess wrong, then developers can override the target location by:
    • Setting PublishFolderType on items. See below for list of valid values for Apple platforms (other platforms may have other valid values).
    • Setting the Link metadata on items (if PublishFolderType is also set, the Link path is relative to the publish folder). This seems to follow how it's already done: Allow CopyToOutputDirectory to have a custom destination path dotnet/msbuild#2795 (comment).
    • Setting CopyToOutputDirectory=Never on items that shouldn't be copied.

Known/valid PublishFolderType values:

  • None: the item won't be copied to the app bundle.
  • RootDirectory: the item will be copied to the root directory of the app bundle.
  • Assembly: the item is copied to where the managed assemblies are located in the app bundle.
  • Resource: items are copied to where resources are located in the app bundle.
  • AppleFramework
    • If the item is a *.framework or *.xcframework directory, these directories will be copied to the app bundle's Frameworks directory.
    • If any of the item's containing directories is a *.framework or *.xcframework, select that directory instead and go to the previous point.
    • Otherwise an error is shown.
  • CompressedAppleFramework: the item is assumed to be a zip file containing one or more *.framework or *.xcframework directories. The zip file will be decompressed, and the *.framework and *.xcframework directories treated as AppleFramework items.
  • AppleBindingResource: treated as a third-party binding resource (the exact details are not relevant for this discussion).
  • PlugIns: The item is copied to the PlugIns/ directory for iOS and tvOS, and Contents/PlugIns directory for macOS and Mac Catalyst
  • CompressedPlugIns: The item must be a zip file, which is decompressed, and then treated as PlugIns.
  • An unknown value: we show a warning, and we don't copy the item to the app bundle (i.e. treat it as None).

Example 1

<Content Update="MyImage.png" PublishFolderType="PlugIns" />

would put MyImage.png in MyApp.app/PlugIns/MyImage.png on iOS and tvOS, and MyApp.app/Contents/PlugIns/MyImage.png on macOS and Mac Catalyst.

Example 2

<Content Update="MyImage.png" PublishFolderType="PlugIns" Link="Subfolder/YourImage.png" />

would put MyImage.png in MyApp.app/PlugIns/Subfolder/YourImage.png on iOS and tvOS, and MyApp.app/Contents/PlugIns/Subfolder/YourImage.png on macOS and Mac Catalyst.

Example 3

<Content Update="MyImage.png" Link="Resources/YourImage.png" />

would put MyImage.png in MyApp.app/Resources/YourImage.png on all platforms (and that would be wrong for macOS and Mac Catalyst).

@rolfbjarne rolfbjarne moved this from Needs investigation/decision to September 2021 - In Progress in .NET 6 Sep 27, 2021
@dalexsoto dalexsoto moved this from October 2021 - In Progress to November 2021 - In Progress in .NET 6 Nov 8, 2021
.NET 6 automation moved this from January 2022 - In Progress to January 2022 - Done Jan 17, 2022
rolfbjarne added a commit that referenced this issue Jan 17, 2022
…12572. (#13591)

In .NET, all files that should be published (put into the final .app bundle) are put into the @(ResolvedFileToPublish) item group, and at the end of the build process, .NET will publish all the files in that item group. Which files are in this item group, and how they're put in there, is out of our control (it's just how the build process works in .NET), so we'll have to cope.

Additionally, publishing an app for Apple platforms is different than publishing other .NET apps, because we can't just put all the files in the a directory like .NET usually does, we have a fairly strict directory structure we need to follow, and it also differs between platforms (the structure is different between macOS and iOS for instance).

This means that for every file in the `ResolvedFileToPublish` item group, we have to figure out:

* Should it be put into the app bundle in the first place?
* If so, in which subdirectory (if any)?

This PR implements these changes. The exact details are explained in a document in the PR, but the general logic is:

* We make an educated guess for some types of files we know about (assemblies, unmanaged libraries, images, etc).
* We provide a way to set metadata on each item specifying which type of item it is (assembly, unmanaged library, image, etc), and we'll treat the item as such. This method can also be used to override the guess we made (for files that shouldn't be published for instance).
* We warn if we run into files we're not educated enough to be able to guess about, and for which there's no custom metadata set.

Fixes #12572.
@xamarin xamarin locked as resolved and limited conversation to collaborators Apr 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug If an issue is a bug or a pull request a bug fix dotnet An issue or pull request related to .NET (6) dotnet-pri0 .NET 6: required for stable release request-for-comments The issue is a suggested idea seeking feedback
Projects
No open projects
.NET 6
  
January 2022 - Done
6 participants