ubisoft / Sharpmake

Sharpmake is an open-source C#-based solution for generating project definition files, such as Visual Studio projects and solutions, GNU makefiles, Xcode projects, etc.
Apache License 2.0
927 stars 171 forks source link

Files with identical names in different subdirectories don't build in Xcode projects. #301

Closed greenpeel closed 11 months ago

greenpeel commented 12 months ago

Hey,

When trying to build spirv-tools I wrote a sharpmake script which included everything under the 'source' subdirectory, which works fine when generating a visual studio project. However it doesn't in Xcode, looking closer at the generated project it seems like the second occurrence of a file that has an identical name isn't configured to build (in this case different 'basic_block.cpp' files exist under the two folders 'opt' and 'val').

The folder structure looks something like this:

In the generated Xcode project file the PBXSourcesBuildPhase section only has a single 'basic_block.cpp' entry, if I manually associate the second 'basic_block.cpp' with the spirv-tools target then a second one is added, so it does seem to be an issue with the generation of the file.

The sharpmake project itself, which works with visual studio on windows, doesn't do anything particularly suspicious:

Name = "SpirvTools";
SourceRootPath = Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-tools");
SourceFilesExcludeRegex.Add(Sharpmake.Util.RegexPathCombine("spirv-tools", "source", "link"));
SourceFilesExcludeRegex.Add(Sharpmake.Util.RegexPathCombine("spirv-tools", "source", "lint"));
SourceFilesExcludeRegex.Add(Sharpmake.Util.RegexPathCombine("spirv-tools", "source", "diff"));

// Custom registration for ObjectFileName redirects across all configurations
AddObjFileRedirect(Path.Combine("source", "val", "basic_block.cpp"), "basic_block_val.obj");
AddObjFileRedirect(Path.Combine("source", "val", "function.cpp"), "function_val.obj");
AddObjFileRedirect(Path.Combine("source", "val", "instruction.cpp"), "instruction_val.obj");

This is happening on the latest release, Any ideas?

jspelletier commented 11 months ago

Hi, There was lots of changes recently for xcode. Could you try some other old versions to see if the problem is the same?

greenpeel commented 11 months ago

I tried downgrading to 0.26 and then 0.24 and the issue occurs on all of them, do you need me to go further back or does that rule out the recent changes?

jspelletier commented 11 months ago

no, the changes are more recent than that.

jspelletier commented 11 months ago

How are you compiling? are you compiling with fastbuild ?

greenpeel commented 11 months ago

Nope, not using fastbuild.

greenpeel commented 11 months ago

I collapsed my project into a single project definition below so that you can see everything it does for Xcode/Mac:

using Sharpmake;
using System.IO;
using System.Xml.Linq;

[Generate]
public class SpirvTools : Project
{
    public SpirvTools()
        : base(typeof(CustomTarget))
    {
       AddTargets(new CustomTarget
                    {
                        Platform = Platform.mac,
                        DevEnv = DevEnv.xcode,
                        BuildConfig = BuildConfig.Debug | BuildConfig.Release | BuildConfig.Distribution
                    });

        Name = "SpirvTools";

        IsFileNameToLower = false;
        IsTargetFileNameToLower = false;

        if(Sharpmake.Util.GetExecutingPlatform() == Platform.mac)
        {
            SourceFilesExtensions.Add(".m", ".mm", ".metal", ".plist", ".storyboard", ".xcassets");
            SourceFilesCompileExtensions.Add(".m", ".mm", ".metal", ".storyboard", ".xcassets");
            ResourceFilesExtensions.Add(".storyboard", ".xcassets");
        }

        SourceRootPath = Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-tools");
        SourceFilesExcludeRegex.Add(Sharpmake.Util.RegexPathCombine("spirv-tools", "source", "link"));
        SourceFilesExcludeRegex.Add(Sharpmake.Util.RegexPathCombine("spirv-tools", "source", "lint"));
        SourceFilesExcludeRegex.Add(Sharpmake.Util.RegexPathCombine("spirv-tools", "source", "diff"));

        // Custom registration for ObjectFileName redirects across all configurations
        AddObjFileRedirect(Path.Combine("source", "val", "basic_block.cpp"), "basic_block_val.obj");
        AddObjFileRedirect(Path.Combine("source", "val", "function.cpp"), "function_val.obj");
        AddObjFileRedirect(Path.Combine("source", "val", "instruction.cpp"), "instruction_val.obj");
    }

    [Configure]
    public void ConfigureGeneral(Configuration conf, CustomTarget target)
    {
        base.ConfigureGeneral(conf, target);

        conf.ProjectPath = "[project.SharpmakeCsPath]";
        conf.SolutionFolder = "Tools";
        conf.IncludePaths.Add(Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-tools"));
        conf.IncludePaths.Add(Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-tools/source"));
        conf.IncludePaths.Add(Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-tools/include"));
        conf.IncludePaths.Add(Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-tools/include/generated"));
        conf.IncludePaths.Add(Path.Combine("[project.SharpmakeCsPath]", "vendor/spirv-headers/include"));

        conf.Output = Configuration.OutputType.Lib;
        conf.TargetPath = Path.Combine(Globals.RootDir, Util.LibBinDir, "[project.Name]");
        conf.IntermediatePath = Path.Combine(Globals.RootDir, Util.LibIntDir, "[project.Name]");
    }

    [Configure(DevEnv.xcode)]
    public void ConfigureXCode(Configuration conf, CustomTarget target)
    {
        conf.Options.Add(Options.XCode.Compiler.CppLanguageStandard.CPP14);
        conf.Options.Add(Options.XCode.Compiler.OnlyActiveArch.Enable);
    }
}
jspelletier commented 11 months ago

can you paste AddObjFileRedirect code ?

greenpeel commented 11 months ago

Ah yeah sorry, that function looks like this:

        protected void AddObjFileRedirect(string from, string to)
        {
            ObjectFileRedirects[from] = to;
        }

And then it is applied in the base class of a general [Configure] method in the non flattened version of my project by calling this function:

        private void ApplyObjFileRedirects(Configuration conf)
        {
            conf.ObjectFileName = delegate (string from)
            {
                return ObjectFileRedirects.ContainsKey(from) ? Path.Combine("[conf.IntermediatePath]", ObjectFileRedirects[from]) : "";
            };
        }
greenpeel commented 11 months ago

I tried commenting those redirects out and it didn't have any impact on the generated Xcode project.

jspelletier commented 11 months ago

the coworker that does most of the xcode changes is fixing this right now.

jspelletier commented 11 months ago

he reproduced the problem and has a fix. this should be merged pretty soon

greenpeel commented 11 months ago

Awesome, thanks for the speedy response!

jspelletier commented 11 months ago

please try this locally:

    private class ProjectBuildFile : ProjectItem
    {
        public ProjectBuildFile(ProjectFileBase file, string settings = @"")
            : base(ItemSection.PBXBuildFile, file.FullPath, settings)
        {
            File = file;
        }
greenpeel commented 11 months ago

Yep, after applying that patch to XCodeProj.cs the file is included correctly.

jspelletier commented 11 months ago

no official version generated yet but fix has been commited in 4872ee1a3e095111aeb30cd601c772119418a440