9

I am working on a code generator project that creates solution with customizable layers. for now I am able to create the .sln file by writing all the elements in code. but the project files don't need this, they can be edited using MSBuild Project class.

I want to add a Website project template support and etc, so this way I edit the .sln file is not cool, I wanted to know that is there a better way to do this, like MSBuild or something else ?

I saw Programmatically generate Visual Studio Solution that says to use Visual Studio SDK (which is for extending visual studio, writing plugins ...), but there isn't any code sample.

thanks in advance

Community
  • 1
  • 1
Ashkan
  • 3,322
  • 4
  • 36
  • 47
  • 3
    Web Site projects are bad for you. Use Web Application projects, and those can be manipulated with MSBuild for sure. – Svarog Jan 05 '12 at 12:18
  • I asked something similar before: stackoverflow.com/questions/8064675/. Apparently you can do it using EnvDTE, however I had trouble getting that to work – Ron Sijm Jan 05 '12 at 12:18
  • Maybe you could use something like Premake to generate the project files? http://industriousone.com/premake – eandersson Jan 05 '12 at 12:18
  • the code generator should have support of website projects! thanks Ron I will take a look at EnvDTE, maybe that's what i need. – Ashkan Jan 05 '12 at 12:23
  • It looks like EnvDTE does not support WSP too. Premake is in C, I'm using C#. – Ashkan Jan 05 '12 at 12:40
  • @ashkan It supports C#, at least according to their website. – eandersson Jan 05 '12 at 12:45
  • Web site "projects" are not projects. They are different from everything else in Visual Studio. Web Application projects are the thing to use unless your sites are simple. Having the project be generated is not simple, so I suggest you don't support web site "projects". – John Saunders Jan 05 '12 at 23:49
  • @Fuji Yes, my mistake! I checked it out again, its project kinds are limited (not WSP). thanks. – Ashkan Jan 08 '12 at 08:15
  • @John I'm not so proffessional but website projects are based on directories and files, and they can have virtual directories in them, sometimes it's a must-have! – Ashkan Jan 08 '12 at 08:15

3 Answers3

6

Here is how to use SLNTools to create sln file in code. The code is a mess! But it works!

SLN Tools project is able to create sln file from project files. It's open source and it provides UI for editing and merging sln files. In this case you can use the core project and by referencing the 'CWDev.SLNTools.Core.dll' in your project you can write a messy(!) code like this:

NOTE: Some parts of class are not shown here and also There's some custom objects of my own project like Sln, Project and ... that should not be mistaken with SLNTools Objects with same name. I commented whereever needed!

using CWDev.SLNTools.Core;
using SlnFile = CWDev.SLNTools.Core.SolutionFile;
using SlnProject = CWDev.SLNTools.Core.Project;
using SeptaCodeGenerator.Core.IO;

public class SlnFileManager
{
    // * Note: Sln is of my own type.
    Sln _sln;
    // * SolutionFile is the type in SLNTools project that we use
    SlnFile _slnFile;

    public void WriteNewSlnFile(Sln slnObject)
    {
        _sln = slnObject;
        _slnFile = new SlnFile(); // or you can read sln file like below
        using (SolutionFileReader reader = new SolutionFileReader(_sln.ObjectFullPath))
            _slnFile = reader.ReadSolutionFile();
        AddHeaders();
        AddGlobalSections();
        // add projects
        List<SolutionNode> sns = _sln.GetAllSubItemsOf(typeof(SolutionFolder), typeof(ProjectNode));
        foreach (SolutionNode sn in sns)
            _slnFile.Projects.Add(CreateProject(sn));

        using (SolutionFileWriter writer = new SolutionFileWriter(_sln.ObjectFullPath))
            writer.WriteSolutionFile(_slnFile);
    }

    // this is how I create and add project sections, using Section and PropertyLine
    private SlnProject CreateProject(SolutionNode sn)
    {
        List<Section> projectSections = CreateProjectSections(sn);
        List<PropertyLine> versionControlLines = CreateVersionControlLines(sn);
        List<PropertyLine> projectConfigurationPlatformsLines = CreateProjectConfigurationPlatformsLines(sn);

        string parentGuid = (sn.Parent is Sln ? null : sn.Parent.InstanceGuidSt);
        string relativePath = (sn is Project) ? (sn as Project).ProjFile.ObjectRelativePath : sn.ObjectRelativePath;
        return new CWDev.SLNTools.Core.Project(_slnFile, sn.InstanceGuidSt, sn.TypeGuidSt, sn.Name, relativePath, parentGuid,
            projectSections, versionControlLines, projectConfigurationPlatformsLines);
    }

    // this method creates ProjectSections(WebsiteProperties)
    private List<Section> CreateProjectSections(SolutionNode project)
    {
        Section sec = null;
        var lines = new List<PropertyLine>();
        if (project is SolutionFolder)
        {
            List<SolutionNode> files = project.GetAllSubItemsOf(typeof(SolutionFile));
            foreach (SolutionNode item in files)
            {
                // * consideration required
                lines.Add(new PropertyLine(item.ObjectRelativePath, item.ObjectRelativePath));
            }
            if (lines.Count > 0)
                sec = new Section("SolutionItems", "ProjectSection", "preProject", lines);
        }
        // *** here is the code that I wrote to add website projects ***
        else if (project is WebSiteProject)
        {
            var website = project as WebSiteProject;
            if (_sln.IsUnderSourceControl)
            {
                    lines.AddRange(new PropertyLine[]
                    {
                        new PropertyLine("SccProjectName", "\"SAK\""),
                        new PropertyLine("SccAuxPath", "\"SAK\""),
                        new PropertyLine("SccLocalPath", "\"SAK\""),
                        new PropertyLine("SccProvider", "\"SAK\"")
                    });

            }
            if (_sln.SolutionVersion == SolutionFileVersion.V11VS2010)
                lines.Add(new PropertyLine("TargetFrameworkMoniker", "\".NETFramework,Version%3Dv4.0\""));
            else
                lines.Add(new PropertyLine("TargetFramework", "\"3.5\""));
            // add project references
            string projectReferences = "";
            foreach (Project proj in website.ReferencedProjects)
                projectReferences += proj.InstanceGuidSt + "|" + proj.ClassName + ".dll;";
            lines.Add(new PropertyLine("ProjectReferences", "\"" + projectReferences + "\""));

            string debugConf = "Debug.AspNetCompiler.";
            string releaseConf = "Release.AspNetCompiler.";
            string str = debugConf;
            int k = 0;
            while (k < 2)
            {
                // other properties
                lines.AddRange(new PropertyLine[]
                {
                    new PropertyLine(str + "VirtualPath", "\"/" + website.Name + "\""),
                    new PropertyLine(str + "PhysicalPath", "\"" + website.ObjectRelativePath + "\\\""),
                    new PropertyLine(str + "TargetPath", ("\"PrecompiledWeb\\" + website.Name + "\\\"")),
                    new PropertyLine(str + "Updateable", "\"true\""),
                    new PropertyLine(str + "ForceOverwrite", "\"true\""),
                    new PropertyLine(str + "FixedNames", "\"false\""),
                    new PropertyLine(str + "Debug", (k == 0 ? "\"True\"" : "\"False\""))
                });

                if (k++ == 0)
                    str = releaseConf;
            }
            Random rand = new Random();
            lines.Add(new PropertyLine("VWDPort", "\"" + rand.Next(1111, 65000).ToString() + "\""));
            lines.Add(new PropertyLine("DefaultWebSiteLanguage",
                website.Language == SeptaCodeGenerator.Core.Language.CodeLanguage.CSharp ? "\"Visual C#\"" : "\"Visual Basic\""));

            sec = new Section("WebsiteProperties", "ProjectSection", "preProject", lines);
        }
        var sections = new List<Section>();
        if (sec != null)
            sections.Add(sec);
        return sections;
    }
}

Hope it helps.

Ashkan
  • 3,322
  • 4
  • 36
  • 47
1

You can use the IVsSolution interface to create solutions and add projects to them. You pass in the SVsSolution type to the GetService() method to get an instance of that interface like so:

var solution = (IVsSolution)GetService(typeof(SVsSolution));
Charles Lambert
  • 5,042
  • 26
  • 47
  • I think this works! But I kind of need not to use visual sutdio interop, however, it seems there's no tool available exactly for what I need. – Ashkan Jan 08 '12 at 08:25
0

In my solution files, web site projects are stored in the WebsiteProperties project section. These properties are passed into the aspnet_compiler as input parameters depending on the build configuration. Try to search for projectID (GUID) (C9683FA5-AABA-497F-B780-80EFC3EB81F2 in this case) inside your sln files containing website project, what parts of sln file should be updated/generated by your code generator.

Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "MasterData.Host", "..\..\Customs.Framework\Services\MasterDataService\Tests\MasterData.Host", "{C9683FA5-AABA-497F-B780-80EFC3EB81F2}"
    ProjectSection(WebsiteProperties) = preProject
        SccProjectName = "SAK"
        SccAuxPath = "SAK"
        SccLocalPath = "SAK"
        SccProvider = "SAK"
        TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5"
        ProjectReferences = "{C7C785E8-89E6-459A-969C-BFC2BC7E8ED2}|Porthus.Customs.Framework.Services.MasterData.ServiceImplementation.dll;{ADFA3898-854C-4058-8726-523EA8802FFD}|Porthus.Customs.Framework.Services.MasterData.FaultContracts.dll;{9A565742-4843-40A6-A2E3-2B432573D12B}|Porthus.Customs.Framework.Services.MasterData.MessageContracts.dll;{E8B5D2A1-E934-4C85-AB71-A2148050913A}|Porthus.Customs.Framework.Services.MasterData.ServiceContracts.dll;{3C752087-CED2-499F-AF3B-84E1548229CB}|Porthus.Customs.Services.MasterData.BusinessEntities.dll;{437C651C-3C8D-4B3D-B967-E58DBCD83EE4}|Porthus.Customs.Services.MasterData.BusinessLogic.dll;{59BBDE80-6F20-4FB6-A65B-68BC584AD2CB}|Porthus.Customs.Services.MasterData.DataAccess.dll;"
        Debug.AspNetCompiler.VirtualPath = "/MasterData.Host"
        Debug.AspNetCompiler.PhysicalPath = "..\..\Customs.Framework\Services\MasterDataService\Tests\MasterData.Host\"
        Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\MasterData.Host\"
        Debug.AspNetCompiler.Updateable = "true"
        Debug.AspNetCompiler.ForceOverwrite = "true"
        Debug.AspNetCompiler.FixedNames = "false"
        Debug.AspNetCompiler.Debug = "True"
        Release.AspNetCompiler.VirtualPath = "/MasterData.Host"
        Release.AspNetCompiler.PhysicalPath = "..\..\Customs.Framework\Services\MasterDataService\Tests\MasterData.Host\"
        Release.AspNetCompiler.TargetPath = "PrecompiledWeb\MasterData.Host\"
        Release.AspNetCompiler.Updateable = "true"
        Release.AspNetCompiler.ForceOverwrite = "true"
        Release.AspNetCompiler.FixedNames = "false"
        Release.AspNetCompiler.Debug = "False"
        VWDPort = "1333"
        DefaultWebSiteLanguage = "Visual C#"
    EndProjectSection
EndProject

You should update (at least) ProjectConfigurationPlatforms & NestedProjects global sections. This was needed in my case.

I have implemented merge solution MsBuild task. It supports merging of multiple solution files into the one MergedSolution.sln file. It works in on our 20+ solution files containing 500+ projects (c# & website & sql db project files/sections). Output solution file size is 1.3MB :)

EDIT: SLN Tools project is able to create sln file from project files.

How to generate SLN file with custom MSBuild task:

  • Here is good info about solution and project files format.
  • If you have a set of project files, you can generate your solution file by custom MSBuild task (inside Execute method)
  • To read MSBuild project files use Microsoft.Build.BuildEngine.Project class (it is obsolete but I'm still using it) or Microsoft.Build.Evaluation.Project class
Ludwo
  • 6,043
  • 4
  • 32
  • 48
  • thanks, So you did it all yourself, did you come up with a tool, class or something ? – Ashkan Jan 08 '12 at 08:37
  • I'm using custom MSBuild task, but I cannot publish source code - company rules, sorry :( – Ludwo Jan 08 '12 at 09:48
  • Yeah sure, can you give a hint on how to create .sln file with msbuild task? I think this is the best way to create sln files, as I do for project (.??proj) files. If it works, I'm going to mark this as the answer. – Ashkan Jan 08 '12 at 13:36
  • SLN Tools project is what I needed, thank you so much! I'm probably going to add some features to support addition of different projects and references and ... – Ashkan Jan 09 '12 at 09:18