3F / DllExport

.NET DllExport with .NET Core support (aka 3F/DllExport aka DllExport.bat)
MIT License
938 stars 131 forks source link

Adds Post-Processing feature. Related issue #144 #148

Closed 3F closed 4 years ago

3F commented 4 years ago

Closes #144 Continues direction of PR #146

Explanation and details:

This also updates MvsSln (core of this PR) to 2.5.2 since current PR was based on features that were updated in 2.5.2:

3F commented 4 years ago

I implemented backend logic with configurable environment. ANY properties can be populated like for parent projects (depend on) and/or current dependencies where processed.

Currently no GUI but it's planned. Later.

How to play with this today:

  1. We need to define DllExportProcEnv property:

Format: $(SolutionPath);$(MSBuildThisFileFullPath);...populated property names...;....

For example:

<PropertyGroup>
  <DllExportProcEnv>
    $(SolutionPath);$(MSBuildThisFileFullPath);
    TargetDir;
    AssemblyName;
    TargetPath;
    ...
  </DllExportProcEnv>
</PropertyGroup>
  1. We need callable target, named as DllExportPostProc:
<Target Name="DllExportPostProc">

    <!-- Now we can access the following properties and items:

    $(DllExport)     - version
    $(DllExportSln)  - full path to .sln which controls current project
    $(DllExportPrj)  - full path to current project where processed .NET DllExport

    @(DllExportDirX64)    - $(TargetDir)x64\*.*
    @(DllExportDirX86)    - $(TargetDir)x86\*.*
    @(DllExportDirBefore) - $(TargetDir)Before\*.*
    @(DllExportDirAfter)  - $(TargetDir)After\*.*

    @(DllExportDependents + populated property name) 
       - each populated properties from DllExportProcEnv, 
          e.g. DllExportDependentsTargetDir

    @(DllExportDependencies + populated property name) 
      - each populated properties from DllExportProcEnv, 
          e.g. DllExportDependenciesTargetDir

    @(DllExportSeqDependents + populated property name) 
       - each populated properties from DllExportProcEnv, 
          e.g. DllExportSeqDependentsTargetDir

    -->

</Target>
  1. Enjoy.

@NickAcPT

Can you confirm this solution for your case? For example, for the case with #144 we need like this:

<Target Name="DllExportPostProc">

  <Copy SourceFiles="@(DllExportDirX64)" OverwriteReadOnlyFiles="true"
      DestinationFolder="@(DllExportDependentsTargetDir->'%(Identity)x64\')" />

  <Copy SourceFiles="@(DllExportDirX86)" OverwriteReadOnlyFiles="true"
      DestinationFolder="@(DllExportDependentsTargetDir->'%(Identity)x86\')" />

</Target>

Let me know if any bugs.


update: DllExportInvokedPoint renamed as DllExportProcEnv [?]

update2: new DllExportSeqDependents... items [?]

update3: future updates will be on the new wiki page: https://github.com/3F/DllExport/wiki/PostProc#post-processing

NickAcPT commented 4 years ago

@3F Thanks for your work so far.

I've been having some trouble reproducing your results on my side, but it appears to be because I have something misconfigured.

I'm a bit unsure on which project I need to put the DllExportPostProc target into, and what is the best configuration for the DllExportInvokedPoint property.

I'd appreciate for you to explain a bit more on your answer.

Here's my project structure:

3F commented 4 years ago

@NickAcPT

Any modification should be only where DllExport is installed.

That is, for your case:

  1. DllExportPostProc should be placed only for ProjectA (where DllExport)
  2. After, MvsSln will calculate required data (that was mentioned in #144) and populate it inside DllExportPostProc target.
  3. Now DllExportPostProc can be used for many cases including yours. Bonus: Both directions.

and what is the best configuration for the DllExportInvokedPoint property.

Minimal for the case when @(DllExportDependentsTargetDir->'%(Identity)x64\') is $(SolutionPath);$(MSBuildThisFileFullPath);TargetDir

3F commented 4 years ago

Please note: DllExportInvokedPoint renamed as DllExportProcEnv. I also updated my example above

3F commented 4 years ago

@NickAcPT I added basic GUI support for predefined options, please check this out. It covers at least your case.

That is, today some Post-Proc features are not yet available in GUI. Only manual configuring with msbuild as I said before.

Since this PR is mainly for adding an Post-processing support, full GUI will be considered through a new one, and seems like not for 1.7.1. Of course anybody can open PR to get this ASAP. Welcome.

NickAcPT commented 4 years ago

@3F Sorry for the huge delay. Haven't had much time to mess with this for a while.

Thanks a lot for the work you've done. I've ran across a problem when using Fody.Costura (haven't tried yet with your ILMerge post processing step).

Exception ``` DllExportMod: Found method: WinTabby.Hooks.ManagedHook..method public hidebysig static native int 'CaptionPaintBlockHandler'(int32 'nCode', native int 'wParam', native int 'lParam') cil managed exporting as CaptionPaintBlockHandler and index 1 D:\Visual Studio - Projects\WinTabby\packages\DllExport.1.7.0\tools\net.r_eg.DllExport.targets(76,5): error : The given key was not present in the dictionary. [D:\Visual Studio - Projects\WinTabby\WinTabby.Hooks\WinTabby.Hooks.csproj] at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at net.r_eg.MvsSln.Core.ProjectReferences.<>c__DisplayClass15_0.b__0(Item i) in C:\projects\dllexport-ix27o\MvsSln\MvsSln\Core\ProjectReferences.cs:line 114 at System.Collections.Generic.List`1.ForEach(Action`1 action) at net.r_eg.MvsSln.Core.ProjectReferences.InitMap() in C:\projects\dllexport-ix27o\MvsSln\MvsSln\Core\ProjectReferences.cs:line 114 at net.r_eg.MvsSln.Core.ProjectReferences..ctor(ISlnPDManager slndep, IEnumerable`1 xprojects) in C:\projects\dllexport-ix27o\MvsSln\MvsSln\Core\ProjectReferences.cs:line 90 at net.r_eg.MvsSln.Core.SlnParser.Parse(StreamReader reader, SlnItems type) in C:\projects\dllexport-ix27o\MvsSln\MvsSln\Core\SlnParser.cs:line 159 at net.r_eg.MvsSln.Core.SlnParser.Parse(String sln, SlnItems type) in C:\projects\dllexport-ix27o\MvsSln\MvsSln\Core\SlnParser.cs:line 106 at net.r_eg.MvsSln.Sln..ctor(String file, SlnItems type) in C:\projects\dllexport-ix27o\MvsSln\MvsSln\Sln.cs:line 55 at RGiesecke.DllExport.MSBuild.PostProc..ctor(String raw, TaskLoggingHelper log) in C:\projects\dllexport-ix27o\RGiesecke.DllExport.MSBuild\PostProc.cs:line 82 at RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.ExecutePostProc(Boolean ret, TaskLoggingHelper log) in C:\projects\dllexport-ix27o\RGiesecke.DllExport.MSBuild\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:line 508 at RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.Execute() in C:\projects\dllexport-ix27o\RGiesecke.DllExport.MSBuild\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:line 482 ```

I'll update this comment if I find anything else.

3F commented 4 years ago

@NickAcPT Thank you for the important information!

What build or commit have you tried? Is this a new error after https://github.com/3F/DllExport/pull/148#issuecomment-622140427 ?

Can you please use /v:diag to show more details after DllExportMod: Found method: lines? Or better, can you attach WinTabby.Hooks.csproj file?

3F commented 4 years ago

Actually I'm watching ProjectReferences problem in your stack trace, ie. more like MvsSln part. I need some details from your .csproj related to ProjectReferences for reproducing on my laptop.

Or it could be a problem with new flags in MvsSln: SlnItems.ProjectDependenciesXml | SlnItems.LoadMinimalDefaultData

Because I tested only this:

NickAcPT commented 4 years ago

@3F

I need some details from your .csproj related to ProjectReferences for reproducing on my laptop.

Sure, which details do you need?

3F commented 4 years ago

@NickAcPT

I need the following:

  1. Full definitions of the all ProjectReference from WinTabby.Hooks.csproj, such as:
    <ProjectReference Include="ClassLibrary2.csproj">
      <Project>{64ad76ca-2c85-4039-b0b3-734cf02b2999}</Project>
      <Name>ClassLibrary2</Name>
    </ProjectReference>
  1. I need definitions of the all ProjectGuid properties (if used) from in WinTabby.Hooks.csproj, such as:
<ProjectGuid>{6CE57BB1-4A6D-4714-B775-74A3637EC992}</ProjectGuid>
  1. I need Project sections from your .sln, such as:
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary2", "ClassLibrary2.csproj", "{64AD76CA-2C85-4039-B0B3-734CF02B2999}"
EndProject
  1. Also ProjectSection(ProjectDependencies) from .sln (if used) should be inside Project sections above.

Thanks,

3F commented 4 years ago

@NickAcPT I found and fixed two related errors in MvsSln project. Please try with 1bd7675

NickAcPT commented 4 years ago

@NickAcPT I found and fixed two related errors in MvsSln project. Please try with 1bd7675

I've tried it and came across this error. Here is the project file along with the solution file.

3F commented 4 years ago

Thanks for the reply,

  1. Just to be sure, can you show result for the command DllExport -build-info
  2. About ProjectReference my bad, I meant the dependent project. More like WinTabby.DaemonWPF\WinTabby.Daemon.csproj. Can you attach this? or at least its ProjectReference record.
NickAcPT commented 4 years ago
  • Just to be sure, can you show result for the command DllExport -build-info There we go:
    
    S_NUM_REV:          1.7.0.1056
    S_REL:
    bSha1:              a2a0c69
    bName:              master
    MetaCor:            netstandard1.1
    MetaLib:            v2.0
    Wizard:             v4.0
    Configuration:      PublicRelease
    Platform:           Any CPU
    cfgname:            Release
    revDeltaBase:       2016/10/12
    revDeltaMin:        1000
    revDeltaMax:        65534
:: generated by a vsSolutionBuildEvent 1.14.0.36854


> 2\. About `ProjectReference` my bad, I meant the dependent project. More like `WinTabby.DaemonWPF\WinTabby.Daemon.csproj`. Can you attach this? or at least its `ProjectReference` record.

Oh, no problem 😄 
Here are the files:
[`WinTabby.Events`.csproj](https://gist.github.com/NickAcPT/4954d54277881e4e89f3f8f05d8ae95e) depends on `WinTabby.Hooks`.
And then [`WinTabby.Daemon`.csproj](https://gist.github.com/NickAcPT/cf169827badde868d17cec071695a843) depends on `WinTabby.Events`.
3F commented 4 years ago

@NickAcPT Thank you for the detailed information. Yep, I didn't think first about obsolete projectguid in ProjectReference, but yes, MS avoids an obsolete projectguid in any modern places. My bad. And my issue btw: https://github.com/3F/MvsSln/issues/18

So, this is it, MvsSln expects this format:

<ProjectReference Include="..\WinTabby.Hooks\WinTabby.Hooks.csproj" Project="18816D42-416D-4D2B-83F5-1DF7353184AD" />

But the new is:

<ProjectReference Include="..\WinTabby.Hooks\WinTabby.Hooks.csproj" />

The error is controlled by https://github.com/3F/MvsSln/issues/26

3F commented 4 years ago

Added ProjectReference support without obsolete projectguid. Please confirm solution e638674cf

NickAcPT commented 4 years ago

Can confirm it builds now! Thanks a lot for all the work!

NickAcPT commented 4 years ago

I've noticed a problem that doesn't go away after cleaning and rebuilding when the solution is structured as this:

My end-goal with ProjectC referencing ProjectA is to have it so it also would copy the x64/x86 folders to the output of the ProjectC.

Edit: forgot to attach the build error. out.txt

NickAcPT commented 4 years ago

I'm not sure if it is related to the PR, but on the Pre-processing tab there is a task to run ILMerge, even on .NET Core/Standard projects, but it only works on .NET Framework projects. Would it be possible to make something that can work everywhere (without the need of changing the code of ILMerge)?

I've read here that there's a way to make .NET Core executables/dlls into one single file with some changes, but it requires publishing the project (dotnet publish).

3F commented 4 years ago

Well, I forgot about recursion for multiple destination in msbuild task :)

That is, instead of @(DllExportDependentsTargetDir->'%(Identity)x86\') we need to use, for example, like Outputs splitting etc.

Since DllExportPostProc is a common case for Post-processing feature and Outputs should not interfere with it, for the case like yours and so on, we'll use additional target like:

<Target AfterTargets="DllExportPostProc" Name="DllExportPostProcForDependent"
      Outputs="%(DllExportDependentsTargetDir.Identity)">

<Copy SourceFiles="@(DllExportDirX64)" OverwriteReadOnlyFiles="true"
        DestinationFolder="%(DllExportDependentsTargetDir.Identity)\x64\" />

        ...
</Target>

As you can see, any other custom targets can easily extend our DllExportPostProc

So, I'll update logic ASAP. You can also try it yourself to make sure before I start.

3F commented 4 years ago

I'm not sure if it is related to the PR, but on the Pre-processing tab there is a task to run ILMerge, even on .NET Core/Standard projects, but it only works on .NET Framework projects.

As I remember, I tested both types including .NET Core based projects. Or please clarify the case. Better to open a new issue if you found error related to the other planned Pre-processing feature.

3F commented 4 years ago

I updated logic for generating derivative targets as I mentioned above.

@NickAcPT Try this 0b099166

NickAcPT commented 4 years ago

I tried it.

  • ProjectA (WinTabby.Hooks; .NET Framework 4.8; uses DllExport)
  • ProjectB (WinTabby.Events; .NET Core 3.1; references ProjectA)
  • ProjectC (WinTabby.Daemon; .NET Core 3.1; references ProjectA and ProjectB)

Was I supposed to see any changes on ProjectC? Don't think it created a task there.

Even if not. Tried it and on ProjectA, I see these two targets:

<Target Name="DllExportPostProc" Label="8337224c9ad9e356" />
<Target Name="DllExportPostProcForTargetDir" AfterTargets="DllExportPostProc" Label="8337224c9ad9e356" Outputs="%(DllExportDependentsTargetDir.Identity)">
<Copy SourceFiles="@(DllExportDirX86)" DestinationFolder="%(DllExportDependentsTargetDir.Identity)x86\" OverwriteReadOnlyFiles="true" />
<Copy SourceFiles="@(DllExportDirX64)" DestinationFolder="%(DllExportDependentsTargetDir.Identity)x64\" OverwriteReadOnlyFiles="true" />
</Target>
3F commented 4 years ago

Tried it and on ProjectA, I see these two targets:

It looks correct.

Any data should be configured only in ProjectA!

While ProjectB + ProjectC will magically receive x86+x64 folders from ProjectA. Please attach /v:d log. For me it works well.

NickAcPT commented 4 years ago

There you go. out.txt

3F commented 4 years ago

I get it, we're talking about different cases when you're talking about ProjectC ... references ProjectA and ProjectB

Because:

ProjectC -> ProjectA + ProjectB
ProjectB -> ProjectA

Is not the same to:

ProjectC -> ProjectB -> ProjectA

That's more complex way because it also can be as:

... ProjectE -> ProjectD -> ProjectC -> ProjectB -> ProjectA

And I have a lot of questions if this type. For example,

Well, don't know...

3F commented 4 years ago

Okay, I also added DllExportSeqDependents... items:

    @(DllExportSeqDependents + populated property name) 
       - each populated properties from DllExportProcEnv, 
          e.g. DllExportSeqDependentsTargetDir

This includes sequential referencing through other projects. For GUI find [ ] ++ button (0x10 bit in DllExportPostProcType)

See fc5344f6749

However, for other specific controls and options you need to open new PR. I will not personally consider this at least here.

NickAcPT commented 4 years ago

Can confirm that it works. Thank you so much for all the work you've done. 👍

3F commented 4 years ago

Good! Thanks for the confirmation.