Wednesday, September 15, 2010

MSBuild: Copy a list of files to a list of directories

I have config files in a master directory (team does not allow svn:externals, which I believed was designed for this task, perhaps I did not set it up correctly) that I need to svn update and copy to my modules.


 <PropertyGroup>
  <LocalBranchPath>C:\branches\November<\LocalBranchPath>
  <LocalSourcePath>$(LocalBranchPath)\Source<\LocalSourcePath>
  <TortoisePath>C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe<\TortoisePath>
  <ClientConfigPath>$(LocalSourcePath)\MasterDirectory<\ClientConfigPath>
  <TortoiseUpdate>&quot;$(TortoisePath)&quot; /closeonend:1 /command:update /path:<\TortoiseUpdate>
  <ReferencedAssembliesPath>$(LocalSourcePath)\ReferencedAssemblies<\ReferencedAssembliesPath>
  <DbProjLocalPath>$(LocalBranchPath)\Database\DBProjects<\DbProjLocalPath>
  <PropertyGroup>
<ItemGroup>
 <MyModules Include="$(LocalSourcePath)\CommonControls\LimitsInformation;
  $(LocalSourcePath)\CommonControls\BalanceInquiry;
  $(LocalSourcePath)\CommonControls\AuditTrail;
  $(LocalSourcePath)\Administration\Reassignment;
  $(LocalSourcePath)\IndividualControls\Configuration\ActivityList"
 />   
</ItemGroup>

 I need the config files in ClientConfigPath

to be copied to the subfolder of MyModules called ModuleName.UnitTest





let's get the svn update out of the way 





<Target Name="SvnUpdateClientConfigs">
 <Exec Command="$(TortoiseUpdate)&quot;$(ClientConfigPath)&quot;"
/><Target>

Very short and sweet, now the hard part, copying multiple files to multiple subdirectories. I had no idea FileName was a shortcut in this case for the final directory name only, it came in handy.





<Target Name="CopyClientConfigsBatched" Outputs="%(MyModules.FullPath)">
  <Message Text="@(MyModules -> '%(FullPath)\%(FileName).UnitTest')"/>
 <ItemGroup>
  <ClientConfigs
  Include="$(ClientConfigPath)\*.config"
  Exclude="$(ClientConfigPath)\P*.config" >
  </ClientConfigs>
 <ItemGroup>
 <Copy SourceFiles="@(ClientConfigs)" DestinationFolder="@(MyModules -> '%(FullPath)\%(FileName).UnitTest')"
   SkipUnchangedFiles="true"/>
<Target/>





The Outputs was necessary because Copy.DestinationFolder will not accept multiple items.
So first I build the list of files, which could have been done at the project level but this was the only target that needed it, and those files may not be present until the svn update was run.  The Exclude prevents PolicyCache.config from being part of the copy. SkipUnchangedFiles makes sure files that haven't changed aren't copied wasting time.




Thursday, September 9, 2010

My first Rx attempt

I think I overshot for a first attempt by far and I'm not sure how to validate the efficiency/concurrency of the code. However, here is Rx that so far as I've used it appears to correctly do recursive producer-consumer iteration of a call graph.



 public override ProblemCollection Check(TypeNode type)
    {
      Debug.WriteLine("Checking type:"+type.FullName);
      var initializer = type.Members.OfType<Method>( ).FirstOrDefault(x => x.FullName==type.FullName+".InitializeComponent");
 
      if (initializer==null)
        return null;
      Debug.WriteLine(initializer.FullName);
      var constructorsWithNoInitCall = type.Members.OfType<Method>( ).Where(m => m.NodeType==NodeType.InstanceInitializer).ToList( );
      var visitedMethods = new HashSet<string>( );
      var foundMethods = new ObservableHashSet<Method>( );
 
      var whenMethodsFound = Observable.FromEvent<NotifyCollectionChangedEventArgs>(foundMethods, "CollectionChanged");
 
      whenMethodsFound.Subscribe(
        e =>
        {
          switch (e.EventArgs.Action)
          {
            case NotifyCollectionChangedAction.Add:
              if (constructorsWithNoInitCall.Any( ))
                Parallel.ForEach(e.EventArgs.NewItems.Cast<Method>( ).Where(m => visitedMethods.Any(v => v==m.FullName)==false),
                  i =>
                  {
 
                    lock (visitedMethods)
                    {
                      if (visitedMethods.Contains(i.FullName))
                        return;
                      visitedMethods.Add(i.FullName);
 
                    }
                    Debug.WriteLine("Visiting:"+i.FullName);
                    var callers = (CallGraph.CallersFor(i));
                    constructorsWithNoInitCall.RemoveAll(x => callers.Any(c => x.FullName==c.FullName));
                    if (constructorsWithNoInitCall.Any( ))
                      foreach (var item in callers.Where(c => visitedMethods.Any(v => v==c.FullName)==false))
                      {
                        foundMethods.Add(item);
                      }
                  });
              break;
            default:
              break;
          }
        }
        );
      foundMethods.Add(initializer);
 
 
      ReportProblem(constructorsWithNoInitCall, type);
 
      return Problems;
    }

Vs2010 Code Analysis Custom Rules

I've written my first custom rules for visual studio to help me on my team with the written and unwritten rules. It was a long journey but I have the hang of it. These rules are set to run on every debug build I do and take about 8 seconds to run all.

 As I understand it custom rules ( Inherited from Microsoft.FxCop.Sdk) can also be used as code check in policies on TFS or in the build process to produce a nice xml report.

Here's the base class (.net 4.0 class library project type)


using Microsoft.FxCop.Sdk;
 
public abstract class BaseRule:BaseIntrospectionRule
{
 protected BaseRule(string ruleName)
  : base(
   ruleName,
   //typeof(BaseRule).Assembly.GetName().Name+".Rules"
   "ProjectNamespace.RuleMetadata",
   typeof(BaseRule).Assembly
   ) { }
}


This class was defined without a namespace, I believe it did not work while it was in one. It's also quite important to note that the string literal Must match your project namespace+xml filename without the .xml on it. The commented method may work as well if your xml file is called Rules.xml. This file must be set to Embedded Resource.

Rules can be set as Messages, Warnings, or Errors and show up in the standard Error List control if code analysis is turned on in your project.

The general classes of rules I have written so far include:

  • Do not use method - Things like GC.AddMemoryPressure, MessageBox.Show (we use a custom dialog with styling)
  • Do not inherit - We don't want anyone inheriting from Windows.Forms.Form
  • Do not raise- We don't want code that has NotImplementedExceptions still present.
  • UseHungarian for controls
    • uses a dictionary to determine if the control has a preferred name we use on the team.
  • Fields should be private
  • Control Property must be set to x
    • For example, in most cases we want all dialog border styles to be fixed - I check the Initialize Component method of anything that inherits from Form for the property to be set there.
  • MustOverride - For legacy reasons (old Visual Studio bug they tell me) we have methods/properties that are virtual instead of abstract, and if they aren't overridden will throw a runtime exception.