@ -3,65 +3,43 @@
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Diagnostics ;
using System.Linq ;
using System.Linq ;
using System.Threading.Tasks ;
using ICSharpCode.Core ;
using ICSharpCode.Core ;
using System.Diagnostics ;
using ICSharpCode.SharpDevelop.Gui ;
using System.ComponentModel ;
namespace ICSharpCode.SharpDevelop.Project
namespace ICSharpCode.SharpDevelop.Project
{
{
public enum BuildOnExecuteSetting
{
[Description("${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.WhenRunning.DoNotBuild}")]
DoNotBuild ,
[Description("${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.WhenRunning.BuildOnlyModified}")]
BuildOnlyModified ,
[Description("${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.WhenRunning.BuildModifiedAndDependent}")]
BuildModifiedAndDependent ,
[Description("${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.WhenRunning.RegularBuild}")]
RegularBuild
}
/// <summary>
/// <summary>
/// Tracks changes to projects and causes only modified projects
/// Tracks changes to projects and causes only modified projects
/// to be recompiled.
/// to be recompiled.
/// </summary>
/// </summary>
static class BuildModifiedProjectsOnlyService
class BuildModifiedProjectsOnlyService
{
{
public static BuildOnExecuteSetting Setting {
get { return PropertyService . Get ( "BuildOnExecute" , BuildOnExecuteSetting . RegularBuild ) ; }
set { PropertyService . Set ( "BuildOnExecute" , value ) ; }
}
static readonly Dictionary < IProject , CompilationPass > unmodifiedProjects = new Dictionary < IProject , CompilationPass > ( ) ;
static readonly Dictionary < IProject , CompilationPass > unmodifiedProjects = new Dictionary < IProject , CompilationPass > ( ) ;
stat ic BuildModifiedProjectsOnlyService ( )
public BuildModifiedProjectsOnlyService ( IBuildService buildService )
{
{
// these actions cause a full recompilation:
// these actions cause a full recompilation:
ProjectService . SolutionClosed + = MarkAllForRecompilation ;
ProjectService . SolutionClosed + = MarkAllForRecompilation ;
ProjectService . SolutionConfigurationChanged + = MarkAllForRecompilation ;
ProjectService . SolutionConfigurationChanged + = MarkAllForRecompilation ;
ProjectService . SolutionSaved + = MarkAllForRecompilation ;
ProjectService . SolutionSaved + = MarkAllForRecompilation ;
Project Service. BuildFinished + = Project Service_BuildFinished;
build Service. BuildFinished + = Build Service_BuildFinished;
FileUtility . FileSaved + = OnFileSaved ;
FileUtility . FileSaved + = OnFileSaved ;
}
}
public static void Initialize ( )
void BuildService_BuildFinished ( object sender , BuildEventArgs e )
{
// first call to init causes static ctor calls
}
static void ProjectService_BuildFinished ( object sender , BuildEventArgs e )
{
{
// at the end of an successful build, mark all built projects as unmodified
// at the end of an successful build, mark all built projects as unmodified
if ( e . Results . Result = = BuildResultCode . Success ) {
if ( e . Results . Result = = BuildResultCode . Success ) {
lock ( unmodifiedProjects ) {
lock ( unmodifiedProjects ) {
CompilationPass pass = new CompilationPass ( ) ;
CompilationPass pass = new CompilationPass ( ) ;
foreach ( IBuildable b in e . Results . BuiltProjects ) {
foreach ( IProject p in e . Projects ) {
IProject p = GetProjectFromBuildable ( b ) ;
unmodifiedProjects [ p ] = pass ;
if ( p ! = null ) {
unmodifiedProjects [ p ] = pass ;
}
}
}
}
}
}
}
@ -73,21 +51,14 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
}
}
static IProject GetProjectFromBuildable ( IBuildable b )
void MarkAllForRecompilation ( object sender , EventArgs e )
{
while ( b is Wrapper )
b = ( ( Wrapper ) b ) . wrapped ;
return b as IProject ;
}
static void MarkAllForRecompilation ( object sender , EventArgs e )
{
{
lock ( unmodifiedProjects ) {
lock ( unmodifiedProjects ) {
unmodifiedProjects . Clear ( ) ;
unmodifiedProjects . Clear ( ) ;
}
}
}
}
static void OnFileSaved ( object sender , FileNameEventArgs e )
void OnFileSaved ( object sender , FileNameEventArgs e )
{
{
if ( ProjectService . OpenSolution ! = null ) {
if ( ProjectService . OpenSolution ! = null ) {
foreach ( IProject p in ProjectService . OpenSolution . Projects ) {
foreach ( IProject p in ProjectService . OpenSolution . Projects ) {
@ -100,20 +71,20 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
}
}
public static IBuildable WrapBuildable ( IBuildable buildable )
public IBuildable WrapBuildable ( IBuildable buildable , BuildDetection setting )
{
{
switch ( S etting) {
switch ( s etting) {
case BuildOnExecuteSetting . DoNotBuild :
case BuildDetection . DoNotBuild :
return new DummyBuildable ( buildable ) ;
return new DummyBuildable ( buildable ) ;
case BuildOnExecuteSetting . BuildModifiedAndDependent :
case BuildDetection . BuildModifiedAndDependent :
case BuildOnExecuteSetting . BuildOnlyModified :
case BuildDetection . BuildOnlyModified :
lock ( unmodifiedProjects ) {
lock ( unmodifiedProjects ) {
foreach ( var pair in unmodifiedProjects ) {
foreach ( var pair in unmodifiedProjects ) {
LoggingService . Debug ( pair . Key . Name + ": " + pair . Value ) ;
LoggingService . Debug ( pair . Key . Name + ": " + pair . Value ) ;
}
}
}
}
return new WrapperFactory ( ) . GetWrapper ( buildable ) ;
return new WrapperFactory ( setting ) . GetWrapper ( buildable ) ;
case BuildOnExecuteSetting . RegularBuild :
case BuildDetection . RegularBuild :
return buildable ;
return buildable ;
default :
default :
throw new NotSupportedException ( ) ;
throw new NotSupportedException ( ) ;
@ -133,22 +104,19 @@ namespace ICSharpCode.SharpDevelop.Project
get { return wrappedBuildable . Name ; }
get { return wrappedBuildable . Name ; }
}
}
public Solution ParentSolution {
get { return wrappedBuildable . ParentSolution ; }
}
public ProjectBuildOptions CreateProjectBuildOptions ( BuildOptions options , bool isRootBuildable )
public ProjectBuildOptions CreateProjectBuildOptions ( BuildOptions options , bool isRootBuildable )
{
{
return null ;
return null ;
}
}
public ICollection < IBuildable > GetBuildDependencies ( ProjectBuildOptions buildOptions )
public IEnumerable < IBuildable > GetBuildDependencies ( ProjectBuildOptions buildOptions )
{
{
return new IBuildable [ 0 ] ;
return Enumerable . Empty < IBuildable > ( ) ;
}
}
public void StartBuild ( ProjectBuildOptions buildO ptions, IBuildFeedbackSink feedbackSink )
public Task < bool > BuildAsync ( ProjectBuildOptions o ptions, IBuildFeedbackSink feedbackSink , IProgressMonitor progressMonitor )
{
{
return Task . FromResult ( true ) ;
}
}
}
}
@ -171,9 +139,15 @@ namespace ICSharpCode.SharpDevelop.Project
sealed class WrapperFactory
sealed class WrapperFactory
{
{
public readonly BuildDetection Setting ;
public readonly CompilationPass CurrentPass = new CompilationPass ( ) ;
public readonly CompilationPass CurrentPass = new CompilationPass ( ) ;
readonly Dictionary < IBuildable , IBuildable > dict = new Dictionary < IBuildable , IBuildable > ( ) ;
readonly Dictionary < IBuildable , IBuildable > dict = new Dictionary < IBuildable , IBuildable > ( ) ;
public WrapperFactory ( BuildDetection setting )
{
this . Setting = setting ;
}
public IBuildable GetWrapper ( IBuildable wrapped )
public IBuildable GetWrapper ( IBuildable wrapped )
{
{
IBuildable b ;
IBuildable b ;
@ -200,10 +174,6 @@ namespace ICSharpCode.SharpDevelop.Project
get { return wrapped . Name ; }
get { return wrapped . Name ; }
}
}
public Solution ParentSolution {
get { return wrapped . ParentSolution ; }
}
public ProjectBuildOptions CreateProjectBuildOptions ( BuildOptions options , bool isRootBuildable )
public ProjectBuildOptions CreateProjectBuildOptions ( BuildOptions options , bool isRootBuildable )
{
{
return wrapped . CreateProjectBuildOptions ( options , isRootBuildable ) ;
return wrapped . CreateProjectBuildOptions ( options , isRootBuildable ) ;
@ -211,8 +181,8 @@ namespace ICSharpCode.SharpDevelop.Project
Dictionary < ProjectBuildOptions , ICollection < IBuildable > > cachedBuildDependencies = new Dictionary < ProjectBuildOptions , ICollection < IBuildable > > ( ) ;
Dictionary < ProjectBuildOptions , ICollection < IBuildable > > cachedBuildDependencies = new Dictionary < ProjectBuildOptions , ICollection < IBuildable > > ( ) ;
ICollection < IBuildable > cachedBuildDependenciesForNullOptions ;
ICollection < IBuildable > cachedBuildDependenciesForNullOptions ;
public ICollection < IBuildable > GetBuildDependencies ( ProjectBuildOptions buildOptions )
public IEnumerable < IBuildable > GetBuildDependencies ( ProjectBuildOptions buildOptions )
{
{
List < IBuildable > result = new List < IBuildable > ( ) ;
List < IBuildable > result = new List < IBuildable > ( ) ;
foreach ( IBuildable b in wrapped . GetBuildDependencies ( buildOptions ) ) {
foreach ( IBuildable b in wrapped . GetBuildDependencies ( buildOptions ) ) {
@ -241,20 +211,20 @@ namespace ICSharpCode.SharpDevelop.Project
return lastCompilationPass . Index > comparisonPass . Index ;
return lastCompilationPass . Index > comparisonPass . Index ;
}
}
public void StartBuild ( ProjectBuildOptions buildO ptions, IBuildFeedbackSink feedbackSink )
public async Task < bool > BuildAsync ( ProjectBuildOptions o ptions, IBuildFeedbackSink feedbackSink , IProgressMonitor progressMonitor )
{
{
IProject p = wrapped as IProject ;
IProject p = wrapped as IProject ;
if ( p = = null ) {
if ( p = = null ) {
wrapped . StartBuild ( buildO ptions, feedbackSink ) ;
return await wrapped . BuildAsync ( o ptions, feedbackSink , progressMonitor ) ;
} else {
} else {
lock ( unmodifiedProjects ) {
lock ( unmodifiedProjects ) {
if ( ! unmodifiedProjects . TryGetValue ( p , out lastCompilationPass ) ) {
if ( ! unmodifiedProjects . TryGetValue ( p , out lastCompilationPass ) ) {
lastCompilationPass = null ;
lastCompilationPass = null ;
}
}
}
}
if ( lastCompilationPass ! = null & & Setting = = BuildOnExecuteSetting . BuildModifiedAndDependent ) {
if ( lastCompilationPass ! = null & & factory . Setting = = BuildDetection . BuildModifiedAndDependent ) {
lock ( cachedBuildDependencies ) {
lock ( cachedBuildDependencies ) {
var dependencies = buildO ptions ! = null ? cachedBuildDependencies [ buildO ptions] : cachedBuildDependenciesForNullOptions ;
var dependencies = o ptions ! = null ? cachedBuildDependencies [ o ptions] : cachedBuildDependenciesForNullOptions ;
if ( dependencies . OfType < Wrapper > ( ) . Any ( w = > w . WasRecompiledAfter ( lastCompilationPass ) ) ) {
if ( dependencies . OfType < Wrapper > ( ) . Any ( w = > w . WasRecompiledAfter ( lastCompilationPass ) ) ) {
lastCompilationPass = null ;
lastCompilationPass = null ;
}
}
@ -265,56 +235,17 @@ namespace ICSharpCode.SharpDevelop.Project
StringParser . Parse ( "${res:MainWindow.CompilerMessages.SkipProjectNoChanges}" ,
StringParser . Parse ( "${res:MainWindow.CompilerMessages.SkipProjectNoChanges}" ,
new StringTagPair ( "Name" , p . Name ) )
new StringTagPair ( "Name" , p . Name ) )
) ;
) ;
feedbackSink . Done ( true ) ;
return true ;
} else {
} else {
lastCompilationPass = factory . CurrentPass ;
lastCompilationPass = factory . CurrentPass ;
wrapped . StartBuild ( buildOptions , new BuildFeedbackSink ( p , feedbackSink , factory . CurrentPass ) ) ;
var success = await wrapped . BuildAsync ( options , feedbackSink , progressMonitor ) ;
}
if ( success ) {
}
lock ( unmodifiedProjects ) {
}
unmodifiedProjects [ p ] = factory . CurrentPass ;
}
/// <summary>
/// Wraps a build feedback sink and marks a project as requiring recompilation when
/// compilation was not successful.
/// </summary>
sealed class BuildFeedbackSink : IBuildFeedbackSink
{
IProject project ;
IBuildFeedbackSink sink ;
CompilationPass currentPass ;
public BuildFeedbackSink ( IProject p , IBuildFeedbackSink sink , CompilationPass currentPass )
{
Debug . Assert ( p ! = null ) ;
Debug . Assert ( sink ! = null ) ;
Debug . Assert ( currentPass ! = null ) ;
this . project = p ;
this . sink = sink ;
this . currentPass = currentPass ;
}
public Gui . IProgressMonitor ProgressMonitor {
get { return sink . ProgressMonitor ; }
}
public void ReportError ( BuildError error )
{
sink . ReportError ( error ) ;
}
public void ReportMessage ( string message )
{
sink . ReportMessage ( message ) ;
}
public void Done ( bool success )
{
if ( success ) {
lock ( unmodifiedProjects ) {
unmodifiedProjects [ project ] = currentPass ;
}
}
return success ;
}
}
sink . Done ( success ) ;
}
}
}
}
}
}