-
Notifications
You must be signed in to change notification settings - Fork 510
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
Comments
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 |
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
When I build, I get the following error:
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.
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. |
@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 |
@SteveBush the problem with an absolute path for BaseOutputPath was a bug (#12224), and the fix will be included in RC 2. |
Correct. The complete output is a directory with the
iOS and tvOS basically place everything in the root .app directory (with a few notable exceptions):
macOS and Mac Catalyst use a few more subdirectories:
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
I guess that could also work. |
That would be useful for wasm too. For example - we want to differentiate |
@dsplaisted ping as requested! |
I prefer option 1. I see this useful for at least all mobile configurations. |
@dsplaisted just a friendly ping here! |
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 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 ( <Content Update="MyImage.png" PublishFolderType="Plugins"/> That would mean having a list of valid values for |
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 Anyways, my updated suggestion would be to implement the following (both 1 and 2 combined, they're not mutually exclusive):
Known/valid
Example 1<Content Update="MyImage.png" PublishFolderType="PlugIns" /> would put MyImage.png in Example 2<Content Update="MyImage.png" PublishFolderType="PlugIns" Link="Subfolder/YourImage.png" /> would put MyImage.png in Example 3<Content Update="MyImage.png" Link="Resources/YourImage.png" /> would put MyImage.png in |
…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.
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:
xamarin-macios/dotnet/targets/Xamarin.Shared.Sdk.targets
Lines 995 to 1013 in cf491b0
And here we also remove files that shouldn't be published (but are needed at some point during the build):
xamarin-macios/dotnet/targets/Xamarin.Shared.Sdk.targets
Lines 1015 to 1059 in cf491b0
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:@(None)
,@(Content)
and@(EmbeddedResource)
items with theCopyToOutputDirectory
or theCopyToPublishDirectory
metadata set. It goes like this:TargetPath
metadata in theAssignTargetPaths
targetGetCopyToPublishDirectoryItems
target then filters on 'CopyToPublishDirectory' and assigns to@(_SourceItemsToCopyToPublishDirectory)
._ComputeCopyToPublishDirectoryItems
target adds@(_SourceItemsToCopyToPublishDirectory)
to@(ResolvedFileToPublish)
ResolvePackageAssets
adds to theNativeCopyLocalItems
groupResolveLockFileCopyLocalFiles
adds it toReferenceCopyLocalPaths
$(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).
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).
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).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 theAppBundleLocation
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).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).
The text was updated successfully, but these errors were encountered: