Download Genuilder-1.1-bin.zip - 1.03 MB
Download Genuilder-1.1-src.zip - 898.72 KB
Genuilder's home in Codeplex
Table of contents
Introduction:
First of all, I am very excited to talk about this project, this is maybe the most
useful I have ever done (It's not hard stuff, because I love doing
useless things too).
Genuilder (the name is a mix between "Generator" and "Builder") is the beginning
of a new way to easily extend Visual Studio and your build process in 8 mouse clicks
(yeah it's a power of two, a plain round integer)
This idea comes from previous works I've done on MSBuild : the
AppConfigVerifier done with
Daniel, the
AppSettingsGenerator, and a little tool I've done at work, EnvironmentFile,
that I'll present during this article.
I thought that these articles haven't the popularity they deserve, I was very happy
with them but not so many people have shared their enthusiasm in vote. My guess
is that people don't care about MSBuild, so this article is split in two parts.
- What Genuilder do, how to you use it
- Internals
You can read the first part in 2 or 3 minutes. In 10 min from now, your project
will be ready to use Genuilder.
I've done everything to make this project to "just work" in 8 clicks without MSBuild
knowledge.
This project will evolve depending on the enthusiasm and ideas you have, I believe
there is much potential for futur improvements.
Genuilder is on codeplex, I recommend you to download sources and binaries there!
Installation
Imagine you want to install Genuilder on MyCorpProject :

Download the last release.
Copy the Genuilder directory next to your solution :

In the Genuilder directory execute Genuilder.GUI.exe, open the solution and select
your project, then click on the button :

Now MyCorpProject has all the features of Genuilder.
Yes, it's that simple...
Note : The project must not be in read-only
Features
AppConfigVerifier
The AppConfigVerifier is an integration in Genuilder of this project.
It scans all 'type=".*"' in the config file, and check if the type is
referenced in the project.
The documentation can be found on CodeProject
Here is a quick example, where in the config file you misspelled the MembershipProvider
type:

Genuilder will find the error at compile time:

You can exclude types or assemblies from the verification, just check this article
AppSettingsGenerator
This feature comes from one of my articles on CodeProject.
With it you can access to your AppSettings and ConnectionStrings in a strongly typed
way.
Example, the appconfig :

Auto completion on AppSettings :

Auto completion on ConnectionStrings :

EnvironmentFile
The EnvironmentFile is just a pre-processor for your config file. For example you
have this app.config with a token:

You have also this EnvironmentFile.env file which is just a key=value pair list
delimited by line breaks:

After compilation, the output config file (for example MyProject.exe.config) will
be:

This is particulary useful when developpers in a team have not the same development
environment,
they doesn't need to checkout their own version of the app.config. (The EnvironmentFile
should be unique for each developer and should not be in your source control.)
If one of your fellow developer changes the config file like that:

After the next Get latest version, the project will not compile because the new
token is not set in the EnvironmentFile.env:

Note : this feature doesn't work for web.config files. (Don't worry .NET 4.0 is coming
with a new feature for web.config files)
InterfaceExtractor
Interface extractor is a code generator which extract an interface from public members
of classes and structs.
This feature is really useful when you want to create static or dynamic proxies
around a service or a controller for authentication or logging for example.
I'll create an article on codeproject on a nice use case with ASP.NET MVC to control
authorized access of a controller. (I have not done and I've not eat my own dog
food yet... so be really careful with this feature ! ;))
For example this class :

Will create extract this interface :

Note that the parameter of the ExtractInterface is optional, the default interface
name is IClassName.
Also, you don't have to implement the interface on your class.
Warning : [ExtractInterface(typeof(SomeNamespace.Interface))] is currently not allowed,
you cannot specify the namespace of the extracted interface... not a big deal to
add though
PropertiesExtractor
As InterfaceExtractor, PropertiesExtractor is
a class generator which generate a static class to access properties name in a strongly
typed way.
For example this class implement INotifyPropertyChanged and do not hard code the
property name with a string :

Because PropertiesExtractor will generate this other class

This way if you refactor the property name, the code will break at compile time
instead of during runtime.
As with InterfaceExtractor, the parameter of the ExtractProperties is optional,
the default class name is ClassNameProperties.
Warning : [ExtractProperties(typeof(SomeNamespace.MyClassProperties))] is currently
not allowed, you cannot specify the namespace of the extracted class properties...
not a big deal to add though
StrongConfigGenerator
This feature generate a class to access to your configuration file in a strongly
typed way.
If you remove a section whereas your code somewhere has a dependency on it, it will
break the code at compile time instead of during the runtime.
For example for this config file :

You will be able to access to sections like this :

Known problems with Visual Studio
These bugs are fixed in version 1.1.
My code doesn't compile the first time
There is a bug in Visual Studio 2008, when you compile generated classes are generated
before the compilation;
but for some obscure reasons, Visual studio will fail the compilation if you already
use these generated classes in your code, although Intellisense works well and that
the classes are generated.
But the next compilation will not fail.
Example, you code this class in one step before compiling the first time :

Visual studio says :

The next time you modify the file where Person is and rebuild, you won't see the
error.
This is clearly a bug, which doesn't appear when you build with MSBuild in command
line...
It seems that visual studio compile the class generated before you decide to build.
My code should break at compile time but it doesn't
This bug is caused by the same root as
this bug.
You have this class:

You refactor the property's name to 'Year'.
The constructor should break since it references PersonProperties.Age instead of
PersonProperties.Year.
But Visual studio doesn't complain :

The next time you modify the file where Person is and rebuild, it will break as
expected.
How I've fixed these bugs
These bugs was caused by visual studio which uses an inprocess compiler instead of Csc.exe, source :MSBuild Team. The trick was just to add this line in the AllTargets.targets to force Visual Studio to use Csc.exe.
<PropertyGroup>
<UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable>
</PropertyGroup>
Internals
How do you do that ? It's awesome !
I hope you are as enthusiast as me on this project, in fact it is stupid simple.
When you run Genuilder GUI on your project, there is only two lines in your project
file.
I import AllTargets.targets,
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\bin\AllTargets.targets" /> <!---->
And I add a reference on Genuilder.dll (for now it contains only two class,
ExtractInterface and ExtractProperties )
<Reference Include="Genuilder">
<HintPath>..\bin\Genuilder.dll</HintPath>
</Reference>
That's it ! And here is the AllTargets.targets file. If you want to disable a feature
just comment a line.
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="AppConfigVerifier.targets" />
<Import Project="AppSettingsGenerator.targets" />
<Import Project="EnvironmentFile.targets" />
<Import Project="InterfaceExtractor.targets" />
<Import Project="PropertiesExtractor.targets" />
<Import Project="StrongConfigGenerator.targets" />
</Project>
Most of these targets files are not complicated, they insert their targets in the
build process and invoke a task. For example here is AppConfigVerifier.targets (as documented in my previous article AppSettingsGenerator).
Note that StrongConfigGenerator and AppSettingsGenerator are very similar, you can
also change the name and the namespace of generated classes in these files.
="1.0" ="utf-8"
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="AppConfigVerifier.MSBuild.Verifier" AssemblyFile="AppConfigVerfier.MSBuild.dll" />
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
AppConfigVerifier_GetConfigFile;
VerifyAppConfig
</BuildDependsOn>
</PropertyGroup>
<Target Name="AppConfigVerifier_GetConfigFile">
<ItemGroup>
<_ConfigFile Include="@(AppConfigWithTargetPath)" Condition="Exists('@(AppConfigWithTargetPath)')" />
<_ConfigFile Include="Web.config" Condition="Exists('Web.config') And '@(_ConfigFile)' == ''" />
</ItemGroup>
</Target>
<Target Name="VerifyAppConfig" Inputs="@(_ConfigFile)" Outputs="$(IntermediateOutputPath)Verifier_LastCheck.txt" Condition="Exists('@(_ConfigFile)')">
<ItemGroup>
<DefaultReferencePath Include=" @(MainAssembly->'%(FullPath)')"></DefaultReferencePath>
</ItemGroup>
<AppConfigVerifier.MSBuild.Verifier References="@(ReferencePath)" DefaultReferences="@(DefaultReferencePath)" Config="@(_ConfigFile)">
</AppConfigVerifier.MSBuild.Verifier>
<WriteLinesToFile File="$(IntermediateOutputPath)Verifier_LastCheck.txt" Lines="LastModified" Overwrite="true">
</WriteLinesToFile>
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)Verifier_LastCheck.txt"/>
</ItemGroup>
</Target>
</Project>
I use the log of MSBuild to send messages to the Error List of Visual Studio.
In a near future, I will add MSBuild properties to easily disable some targets for
some projects.
So that's it, as you see it's stupid simple why nobody has thought about this earlier
?
Interesting stuff I have done with MSBuild during the development of this project.
My favorite principle in development is DRY, Don't Repeat Yourself. I'll show you
how MSBuild is all about that.
This solution has three projects:
- Genuilder.GUI is just a WPF application, nothing really interesting. (except it's
sexy baby!)
- Genuilder.sln have all projects related to targets files, it's the core project.
- GenuilderSample.sln is a solution with one or two project samples per targets file.
I wanted to automate three things :
- Every targets files should be added to AllTargets.targets. (Do you remember ? it's
the file which is imported in your project file with my GUI)
- Every project with a Genuilder feature (ie, a targets file) must generate their
output in a common directory.
- Every project sample must import the targets file it is supposed to test.
I adopted a convention with the name of my projects.
All projects which contains a new feature to include in the AllTargets.targets are
called *.Targets
All project samples end with XXXXX.TestProject(x?), where XXXXXX is the name of
the tested project "XXXXX.Targets"
For example if I create a new Targets project called NewFeature.Targets, and a new
project sample called NewFeature.TestProject, they will be configured in one keystroke.
With MSBuild I will decrease manual work to do when I create a new project, and
improve readability by following conventions.
The project "SetCommonConfigOnTargets" in Genuilder.sln, is a command line utility
which :
- Import a targets file called "Common.Before.targets" in all .Targets project.
- Import the targets file XXXX.targets in every XXXX.TestProject.csproj.
Here is "Common.Before.targets", it sets the output directory of all .Targets project
to a common directory.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<OutDir>..\bin</OutDir>
</PropertyGroup>
</Project>
During the compilation of the project CommonConfig, SetCommonConfigOnTargets.exe
is called, projects are configured, and AllTargets.targets is created.
CommonConfig references automatically all .Targets projects, this way they are compiled
before CommonConfig. Yeah, it's automatic, and it's one line in the project
file:
<ProjectReference Include="..\*.Targets*\*.csproj" />
I also store every targets files in the a MSBuild item called "TargetsFiles".
<TargetsFiles Include="..\**\*.targets" Exclude="..\**\bin\**\*;..\$(AssemblyName)\*.targets;..\*TestProject*\**\*.targets" />
Then I redefine what it means to "Compile" the project, ie, Create the AllTargets.targets
file, and execute SetCommonConfigOnTargets.exe, to ensure that all projects which
follow the convention are configured.
<Target Name="Compile">
<Message Text="@(TargetsFiles)">
</Message>
<ItemGroup>
<_LinesToWrite Include="<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">" />
<_LinesToWrite Include="@(TargetsFiles -> '<Import Project="%(FileName)%(Extension)" />')" />
<_LinesToWrite Include="</Project>" />
</ItemGroup>
<WriteLinesToFile File="AllTargets.targets" Lines="@(_LinesToWrite)" Overwrite="true">
</WriteLinesToFile>
<Message Text="$(Language)">
</Message>
<Copy SourceFiles="AllTargets.targets" DestinationFolder="$(OutDir)">
</Copy>
<Exec Command="SetCommonConfigOnTargets.exe" WorkingDirectory="..\SetCommonConfigOnTargets\bin\$(Configuration)">
</Exec>
</Target>
I also had to override a build step called CopyFilesToOutputDirectory to make it
works, because CommonConfig doesn't generate an assembly.
<Target Name="CopyFilesToOutputDirectory" />
This way when I compile CommonConfig.csproj, everything is well configured without
manual intervention.
Conclusion:
Well that's it, I wait your feedback and some ideas on new features I should add
to Genuilder. If you like it and if you use it, please spread the word. :D
| You must Sign In to use this message board. |
|
|
 |
|
 |
And a great way to remove code smell. You're definitely one to watch Nico - keep it up.
"WPF has many lovers. It's a veritable porn star!" - Josh Smith As Braveheart once said, "You can take our freedom but you'll never take our Hobnobs!" - Martin Hughes. My blog | My articles | MoXAML PowerToys | Onyx
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Tops
Sacha Barber- Microsoft Visual C# MVP 2008/2009
- Codeproject MVP 2008/2009
Your best friend is you. I'm my best friend too. We share the same views, and hardly ever argue My Blog : sachabarber.net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi Nicolas,
This looks like a very interesting and creative piece of work : I look forward to understanding it (at least partially).
I do think, for those of us not deeply familiar with the ground you are covering, a simple introductory paragraph or two describing what this is, and what it does, would be helpful.
Works in VS 2010 beta 2 ? : Does VS 2010 beta 2 have the same issues you describe VS 2008 as having ?
best, Bill
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
modified on Monday, November 9, 2009 7:37 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Bill, thanks for your feedback !!  If you don't understand what it does, it's my fault because it's really simple.
It works on VS 2010, (at least I don't see why it shouldn't work). Bugs are fixed, I explained how I've fix them for documentation purpose.
I can't explain in one sentence what Genuilder does, because it has lots of features[^] (each features are explained in this article). In fact, it inserts tasks during the build process to add features at compile time (app.config validation and code generation).
All these features are available when you have installed Genuilder for your project as indicated here.[^].
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Nicolas,
Thanks for your gracious replies ! Unfortunately I am just off on a trip to Laos, so it will be several days before I have the opportunity to study your code and try it out. If I have any constructive ideas re how you describe the code, and its functionality, in your article, I will PM you.
Your enthusiasm is great to see 
best, Bill
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
sorry to spam you a second time :p This time I'd like to know do you think I can make this article more understandable ? I think that what is technologicaly difficult is "Internals" part. I didn't want to explain all in details because I've done this article[^], for that.
How do you think I could explain better in the introduction ?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
I didn't understand at what moment exactly the extractors work. I actually use the Post-build event command line to create some interfaces automatically, also having the same problem that the first compilation causes errors and the second one goes fine. But, it looks to me that you didn't use these events. So, where exactly they are called during the compilation process?
I will also say that a PropertyInfoExtractor (and maybe a FieldInfoExtractor) will be great. At least while the .net does not have a "propertyinfoof" keyword, having an automatic (and compile time validated) way to get the propertyinfo is a good choice. I would only say that, even this being a break-change, that PropertyExtractor must extract the PropertyInfo, and rename the actual one to PropertyNameExtractor.
Out of topic: Can you add me in messenger? Mine is paulozemek@hotmail.com
Sorry for the edits in this message, but I must say I think a solution that could be great: PartialExtractor.
The class with this must be marked as partial. But: In it, new sub-classes will be added (Fields, Methods, Properties, Methods, Events), so you can get the ones you want at any moment, with thinks like: MyClass.Properties.PropertyName, MyClass.Methods.MethodX (only non-overloaded methods supported, for obvious reasons), MyClass.Fields.FieldName and MyClass.Events.EventName. (I recently tested, and this works).
I would extend this and say that you can: -> Threat .Properties as public, and then create PrivateProperties (which are, of course, of private type in the class declaration) and ProtectedProperties. So, following the pattern, using your solution and a good architectect, it will be possible to reference to any type, property, field, event or even the name of them in a type safe manner and have everything we want.
I had already proposed Microsoft to create a "type" called interfaceof(Class), so, instead of ISomeType I could reference (interfaceof(SomeClass)) and get the same result from the original class, and from the same "duck-typed" class.
modified on Saturday, November 7, 2009 11:05 PM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I've added you in messenger. I explain how I insert a task during the build here Strongly typed AppSettings with MSBuild.
The trick is here
<PropertyGroup> <ResolveReferencesDependsOn> GenerateSetting; $(ResolveReferencesDependsOn) </ResolveReferencesDependsOn> </PropertyGroup>
You can inject tasks in every step in the build process. ("C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets" describes the build process, it's well documented, but if you are lost I recommend you the book from Sayed Ibrahim Hashimi)
As you can see, I inject a task just before ResolveReferences, which is a little bit counter-intuitive, but was the only way to get the path to the config file in the output directory. (I don't remember why I have not inserted the task just before the compilation, but there was a reason)
In Genuilder, if I don't work on config file, I should insert the generation just before the compilation (for now I generate extractos before ResolveReferences, I should fix that, even if it's not a bug).
Your idea on the PartialExtractor is awesome, why had I not think about it ?  I should seriously refactor what I've done, because for the version 1 I've done the simplest thing that possibly work... design included  I'm also thinking to create a framework with a project template for MSBuild targets, which automatically generate .targets file from the code.
PropertyInfoExtractor seems a good idea too, but now you can already access them in a strongly typed way (typeof(MyClass).GetProperty(MyClassProperties.MyProperty), so for what use case to create PropertyInfoExtractor ?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The PropertyInfoExtractor was my primary idea, but I think the PartialExtractor will be better.
But, I still think that: ClassProperties.Property.Name is better than typeof(Class).GetProperty(ClassProperties.Property) also because in the first case I will only type Class once and, in the second case I could write typeof(Class).GetProperty(OtherClassProperties.Property).
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I think I know the reason of the bugs. Except for the "Rebuild all" (I don't know if the bugs happens there) Visual Studio keeps all the code precompiled (that why refactor works so fast) but, when you auto-generate code during compilation, it may not "see" the changes, as it reads the status of the files just before deciding which files must be compiled.
Well, I think that is the reason. I am not really sure.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
yes it seems that's why, it a pain, it would be great if there was a way to change this behavior... I'll try to add CompilerGeneratedAttribute on generated classes.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
I'VE FOUND A FIX YIPIII !!!!
<PropertyGroup> <UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable> </PropertyGroup>
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Great work Nico! You've done an outstanding job.
I really like the attribute based interface generation. It’s a shame that this approach can’t be used with generic types. It’s strange, but I don’t have intellisense with the generated interface. I wonder if it is a Resharper thing. I’d be interested to know how to resolve the double build requirement. Is that only within Visual Studio? Does a command line build, or a TFS build, work if it hasn’t already been built?
As I mentioned above, I see that one limitation of the attribute based approach is that the type can’t be generic, because the generated interface expects a type parameter, making it impossible to use it in a typeof expression.
[ExtractInterface(typeof(ISomeClass))] class SomeClass<T> But for non-generic classes it’s a cool.
One little thing, and probably easily fixed, is that I see with ExtractProperties, explicit interface implementation can break things. For example:
public interface IFoo { string Bah { get; set; } }
[ExtractProperties(typeof(SomeClassProperties))] class SomeClass<T> : IFoo { public List<string> Strings { get; set; }
public SomeClass() { }
string IFoo.Bah { get; set; }
public string Bah { get; set; } }
This results in a compile error: Error 1 The type 'GenuilderTest.SomeClassProperties' already contains a definition for 'Bah'
I really like the ExtractProperties. I wonder if it is just me that doesn’t have intellisense?
You’ve obviously put a lot of effort into this. Well done! 5 stars from me.
Cheers, Daniel
|
| Sign In·View Thread·PermaLink | 4.00/5 |
|
|
|
 |
|
 |
The double build bug is only on visual studio, no problem with MSBuild in command line ! I heard that visual studio don't use csc.exe to compile the code but it has his own tool internal comile source : http://blogs.msdn.com/msbuild/archive/2005/11/10/489666.aspx[^].
For resharper, as I said : it sucks that where the bug is ! :p No really, maybe I have an idea where it can comes.
For the generic interface, try to write
[ExtractInterface(typeof(ISomeClass<>))] if you have two parameters
[ExtractInterface(typeof(ISomeClass<,>))]
For the PropertyExtract, indeed it's a bug... How do you do to always find a bug in my code everytime in less than 2 minutes, it's crazy ! :p
modified on Sunday, November 8, 2009 4:34 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Ok, my bad. Generics work. That's way cool. I'm well impressed.
Nicolas Dorier wrote: For the PropertyExtract, indeed it's a bug... How do you do to always find a bug in my code everytime in less than 2 minutes, it's crazy ! :p
Because I know where the tricky stuff is.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|