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

Buildalyzer deleting build artifacts #105

Open
hannasm opened this issue Mar 25, 2019 · 12 comments
Open

Buildalyzer deleting build artifacts #105

hannasm opened this issue Mar 25, 2019 · 12 comments
Labels
Discussion/Question Discussions or questions about the code

Comments

@hannasm
Copy link

hannasm commented Mar 25, 2019

I have a buildalyzer based console application which i am using as part of an exec task in my build chain.

The build order works roughly like this

  • project A (project that will be analyzed by buildalyzer) -> project B (buildalyzer console app) -> project C (depends on output of buildalyzer app)
  • project C invokes the buildalyzer app (project B) in a before compile target
  • The buildalyzer console app (B) loads the full solution, and analyzes project A into an adhoc roslyn workspace and performs some code analysis.

The wierd thing is that after the buildalyzer app runs, the dlls for project A (bin/Debug/A.dll) have been deleted. This causes compilation for project C to fail.

I wouldn't expect buildalyzer to have any impact on build artifacts when analyzing a project, so this seems bugworthy.

I managed a workaround by inovking another build of project A after buildalyzer (dotnet build --no-dependencies --no-restore) but it adds to compilation time is definitley suboptimal

@richardwerkman
Copy link

This issue is related to #38 and was fixed in #40. However, it seems that the fix is no longer in place. Maybe it has been refactored out of buildalyzer?

I'm seeing the same issue where I have to compile, run buildalyzer and then compile again because buildalyzer cleaned my dlls. @daveaglick where has the fix from #40 gone? Is there now another way of stopping buildalyzer to clean the project?

@dmirmilshteyn
Copy link

I had a similar requirement (no clean during analysis), and managed to find a way to do so.

var buildEnvironment = projectAnalyzer.EnvironmentFactory.GetBuildEnvironment().WithTargetsToBuild("Build");
var analyzerResults = projectAnalyzer.Build(buildEnvironment);
var project = analyzerResults.Results.First().AddToWorkspace(workspace);

Not sure if this is the "proper" way to do it, but it works for me...

@daveaglick
Copy link
Collaborator

where has the fix from #40 gone?

It was lost during the big 2.x rewrite. While it's important to have the option, I didn't want to make it too easy since not cleaning was a large source of issues before. I got a lot of reports of Buildalyzer not working only to discover that users had turned off the clean target and turning it back on resolved their issues.

Is there now another way of stopping buildalyzer to clean the project?

Yes, it's still relatively east to do. While the approach from @dmirmilshteyn will work, you can actually skip creating the BuildEnvironment yourself and just pass in a EnvironmentOptions:

EnvironmentOptions options = new EnvironmentOptions();
options.TargetsToBuild.Remove("Clean");
var results = projectAnalyzer.Build(options);

I'm certainly open to suggestions on how to make this clearer, including arguments that it really should be a flag on ProjectAnalyzer.

@daveaglick daveaglick added the Discussion/Question Discussions or questions about the code label May 17, 2019
@jonstelly
Copy link
Contributor

jonstelly commented Jul 29, 2019

I believe I was having a similar issue through a different use case. I use buildalyzer to generate some typescript from my C# code. I also use the dotnet watch run command during development and have an IDE or two open (rider and VS Code). Buildalyzer building/cleaning the default obj and bin directories was causing file locking issues and timing issues from the dotnet watch trying to rebuild at the same time.

My solution was to have buildalyzer use different intermediate and output paths like below. These are the correct property names for the new .NET Core csproj based projects, they may be different for classic/legacy csproj based projects. If you're dealing only with .NET Core projects I believe this would work for you too:

            var temp = Path.GetTempPath();
            var list = manager
                .Projects
                .Values
                .Select(p =>
                    {                        
                        //Set build/analysis environment options to prevent fighting with IDE, etc...
                        var options = new EnvironmentOptions();
                        options.EnvironmentVariables["IntermediateOutputPath"] = Path.Combine(temp, "ba", "obj", p.ProjectGuid.ToString());
                        options.EnvironmentVariables["OutputPath"] = Path.Combine(temp, "ba", "bin", p.ProjectGuid.ToString());
                        return p.Build(options)
                            .FirstOrDefault(); //You may not want to do this, may want to omit this and use SelectMany depending on your needs
                    })
                .ToList();
            var adhocWorkspace = new AdhocWorkspace();
            foreach (AnalyzerResult analyzerResult in list)
                analyzerResult.AddToWorkspace(adhocWorkspace);
            return adhocWorkspace;

@Noggog
Copy link

Noggog commented Aug 13, 2020

Heya! Thanks for the tool.

I've been trying to get my setup to not clean, and trying both the provided hints still results in a cleaned output directory.

var manager = new AnalyzerManager();
var proj = manager.GetProject(PathToProject);
var opt = new EnvironmentOptions();
opt.TargetsToBuild.Remove("Clean");
var build = proj.Build();

I confirm all the files exist before this code is run. Run the code, and the output directory is mostly empty.

Did things change since the comments were made last? Or am I doing something improperly here?

@bcollamore
Copy link

I've likewise been trying to not Clean. Context: I am attempting to use https://github.com/stryker-mutator/stryker-net (which uses buildalyzer) in a dockerized GitHub Action, and I do not want to 'restore' in the CI/CD pipeline. (1) It has already been restored, and (2) Re-restoring will not work from within a GitHub Action unless credentials of (all) the private Package Feeds are flowed into the GitHub Action, which renders it specific to the project (more specifically, to the nuget.config) being built.

At first I naively assumed I just needed to flow !/restore (or --no-restore) to Buildalyzer, but that turned out to be not relevant. The real problem is that Buildalyzer decimates the output folder. When stryker subsequently attempts to build with its mutants, it has no choice but to /restore.

I tried removing Clean, and Buildalyzer seemed happy (and it respected the lack of the setting); however, subsequent builds in stryker had issues (e.g., cannot find types such as System.Attributes). I didn't poke hard on this one yet due to Dave's comment above that "not cleaning was a large source of issues before."

Note this was also with setting DesignTime to false. I had expected that this would have solved the problem, as this sets a dozen additional GlobalProperties that I assumed was causing incompatibilities with "regular" builds from stryker..

I also tried @jonstelly's approach. That "worked" in the sense that Buildalyzer respected the output/intermediate folders. I detoured by using relative folders instead of a temp folder, but the generated AssemblyInfo files confused subsequent .NET Core builds with duplicate C# attributes. I then deleted the temporary output so as not to interfere with subsequent builds. But that failed too, because Stryker was using those folders as its output/intermediate locations. That's when I realized this workaround defeated the purpose, at least for stryker's use case, as it seems the whole point of Stryker's usage of Buildalyzer is to learn about these locations. (Also, because it then uses these locations, it suffers from the same original problem that it needs to /restore the DLLs into them. Face palm...)

After all this, I come back to, (1) it's really unfortunate that buildalyzer affects the output folder, and (2) I'm left wondering why stryker uses buildalyzer. It seems to just be collecting various high-level meta information, which is readily available from the Roslyn APIs without actually compiling anything or touching the output folder.

Having said that, I'm new to buildalyzer and stryker, so I'm probably missing something. I do recognize that MS has long history of convoluting this space, and I suspect that the beauty of buildalyzer is that it abstracts away much of that. Which leads me back to: it's really unfortunate that it affects the output folder! Perhaps if it built to a temporary location unbeknownst to clients, and still reported the original settings back to the clients. But maybe that fails other use cases, which needs the actual folder output?

@tstockwell
Copy link

var manager = new AnalyzerManager();
var proj = manager.GetProject(PathToProject);
var opt = new EnvironmentOptions();
opt.TargetsToBuild.Remove("Clean");
var build = proj.Build();

I confirm all the files exist before this code is run. Run the code, and the output directory is mostly empty.

Did things change since the comments were made last? Or am I doing something improperly here?

You forgot to pass the options in the call to proj.Build()

However, I'm doing the same thing (but passing the options) with no success.

@MaxEden
Copy link

MaxEden commented Mar 16, 2021

I can guess you called
analyzer.AddToWorkspace(workspace) or analyzer.GetWorkspace() like I did :)
those methods call build action again and with default options and of course with Clean target untouched.

@faldor20
Copy link

faldor20 commented Jan 9, 2022

I am also having this problem. I can't seem to see what it is doing to delete the output. I can disable the clean part of the build command and it even logs that it is not running the clean command but I still get my build artifacts cleared

@daveaglick
Copy link
Collaborator

@faldor20 See the comment above - did you happen to interact with Roslyn workspaces? If so, that gives me somewhere to start.

This issue seems to have flown under my radar. I'll try to take a closer look this week and see if I can come up with any strategies. As I said earlier, cleaning needs to be the default behavior (Buildalyzer "just" calls MSBuild or dotnet build) but maybe we can do something smarter on repeated analysis or alternate output folders or something.

@curtiskeller
Copy link

@faldor20 @daveaglick I was also hitting this issue, and I may have figured out why this is occurring but I do not fully understand the implications of my "fix". I tried what you mentioned above with removing "Clean" from TargetsToBuild... no dice. But that in combination with setting "options.DesignTime = false" maintained the output directory. So my guess is something in the design time build is not respecting the targets to build.

If this is a horrible idea to set this flag to false. Let me know, but it seems to work for my use case.

@molekp
Copy link

molekp commented Feb 7, 2022

Hi,
I'm having this issue too. I've mentioned it in #110 (comment) but now found this issue. I've tried the first solution (prevent clean target), but did not work with stryker (just like @bcollamore mentioned before).

I will try solution with setting temporary outputs, but I think that Buildalyzer should not touch outputs for default (or have option to receive AnalyzerResult without build at all)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Discussion/Question Discussions or questions about the code
Projects
None yet
Development

No branches or pull requests