Command-Line Building Issues

Topics: DocSites, General Questions
Nov 11, 2008 at 3:24 PM
I am trying to create an automated build for an ASP.NET 3.5 application which includes a DocSite project.  I would like to end up with an MSI installer (web setup project) that includes my application and the documentation website.  To do this, I need to have DocProject add the html output to the project automatically from the command line.  I have set the DocProject properties "Apply all to default", "Include project output default", and "Don't ask me this again" to true.

If I build the project from within Visual Studio, it succeeds and adds the html output to the project automatically.

If I try to build the DocSite project individually or as a part of my Visual Studio solution using devenv from the command line, I get an error,
"C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018: The "BuildDocProject" task failed unexpectedly.
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018: System.ArgumentException: Value does not fall within the expected range.
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at EnvDTE.Windows.Item(Object index)
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.VSEnvironmentHost.get_BuildOutput()
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.VSEnvironmentHost.ShowTrace()
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.DocProjectEnvironment.ShowTrace()
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.Engine.BuildEngine`2.Build()
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.BuildController.Build(IDocProject project, BuildType buildType)
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.BuildController.Build(IDocProject project)
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at DaveSexton.DocProject.MSBuild.BuildDocProject.Execute()
C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.targets(40,5): error MSB4018:    at Microsoft.Build.BuildEngine.TaskEngine.ExecuteInstantiatedTask(EngineProxy engineProxy, ItemBucket bucket, TaskExecutionMode howToExecuteTask, ITask task, Boolean& taskResult)".

If I build the DocSite project using MSBuild, the build succeeds but does not include the project output in the project file.  I cannot build my entire solution with MSBuild as web setup projects are not compatible.  I can do two builds, building the DocSite project with MSBuild and the rest of my soltuion with devenv but the html files will not get included in the MSI because they are not added to the DocSite project.

I feel like I am missing some small link in the chain.  Is there something special I need to do to have the html output included in the project automatically when using MSBuild from the command line?  Is the issue with building a DocSite project from the command line using devenv a known issue, hence the MSBuild support?

Thanks for any help anyone can provide.
Coordinator
Nov 12, 2008 at 6:08 AM
Hi,

Thanks for reporting this error.

I did not consider devenv command-line builds when designing DocProject.  As you can see from the stack trace, when DocProject can retrieve the automation object it assumes that it can get a hold of the VS output window and will direct all output to that.  It's probably failing because VS is providing an automation object although its not actually windowed, hence there's no Output window for DocProject to get.  What should happen, theoretically, is that DocProject should direct output to MSBuild instead, although I'm not sure that it's actually possible to do that in this scenario.  I'll add a workitem and look into it.

As you've discovered already, building on the command-line using MSBuild will skip the internal step that includes build items into the project (this was by design since by default a dialog box is shown, which is not appropriate on a build server).

It's easy to run the code to include build items automatically, although since I didn't test this scenario you'll find a few more issues that prevent it from working as expected if the process is started by MSBuild, so some additional effort is required.  Here are the steps that I took:

  1. Open your project's Build Process Component file (BuildProcess.cs|vb)
  2. Find the BuildCompleted method and insert the following (C#) code at the beginning:

TraceLine();

TraceLine("Including build items in project...");

 

IBuildEngine engine = context.Engine;

IDocProject project = engine.Project;

 

if (engine.IncludeBuildItems())

  // at least one item was added to the project

  project.Save();

<!--EndFragment-->If you build the project now using MSBuild you'll find that it works except for one problem: the Help\Html folder is included in the project as a folder node but the files that it contains are not included.  The code that executes when building inside of VS only has to include the folder and all of its files are included, recursively.  Apparently it doesn't work that way in MSBuild :|

My next guess was to add some more code that will include the HTML files instead of relying on the IncludeBuildItems method:

TraceLine();

TraceLine("Including build items in project...");

 

IBuildEngine engine = context.Engine;

IDocProject project = engine.Project;

 

bool includedAny = engine.IncludeBuildItems();

 

foreach (DocProjectItemBase buildItem in engine.BuildItems)

{

  DocProjectFolder folder = buildItem as DocProjectFolder;

 

  if (folder != null)

  {

    foreach (System.IO.FileSystemInfo info in folder.Path.GetFileSystemInfos("*", "*.*", true))

    {

      IProjectItem item;

 

      System.IO.FileInfo file = info as System.IO.FileInfo;

 

      if (file != null)

        item = project.GetItem(file.FullName);

      else

        item = project.GetFolder(file.FullName);

 

      if (!item.IsVisible)

      {

        includedAny = true;

        item.Show();

      }

    }

  }

}

 

if (includedAny)

  project.Save();

The above code doesn't work because I never marked the DocProjectItemCollection class (accessed via engine.BuildItems) as serializable, so it cannot be marshaled to the Build Process Component.

My next try was to simply hard-code the path to the HTML folder as in the following code: <!--EndFragment-->

TraceLine();

TraceLine("Including HTML output in project...");

 

IBuildEngine engine = context.Engine;

IDocProject project = engine.Project;

 

bool includedAny = false;

 

SystemPath htmlFiles = new SystemPath(project.Directory, @"Help\Html\*.htm", true);

 

foreach (string file in htmlFiles.GetFilePaths())

{

  IProjectItem item = project.GetItem(file);

 

  if (!item.IsVisible)

  {

    includedAny = true;

    item.Show();

  }

}

 

if (includedAny)

  project.Save();

<!--EndFragment-->Although that code didn't work either because MSBuildProjectItem, which derives from ProjectItemBase, which implements IProjectItem, is not serializable.  (It can't be since it encapsulates state that's not serializable either, but if ProjectItemBase had subclassed MarshalByRefObject then this code would have worked.)

I've fixed the serialization issues for the next release so that the code snippets above will work.  But for now though it seems you'll have to add the changes to DocProject's source code and then rebuild it yourself.  The only change that needs to be made for the last snippet to work is to derive ProjectItemBase from MarshalByRefObject so that the class definition looks like this:

public abstract class ProjectItemBase : MarshalByRefObject, IProjectItem

<!--EndFragment-->ProjectItemBase Class
http://www.codeplex.com/DocProject/SourceControl/FileView.aspx?itemId=454126&changeSetId=34004


How To Use The Source Code
http://www.codeplex.com/DocProject/Wiki/View.aspx?title=How%20To%20Use%20The%20Source%20Code

Alternatively, since the project file is XML, you could just open it using LINQ to XML or XmlDocument and then add the appropriate elements without having to touch DocProject's codebase.  This could be done in your project's Build Process Component in place of the portion of the code snippets that use project.GetItem.

- Dave

Coordinator
Nov 12, 2008 at 6:11 AM
Edited Nov 12, 2008 at 6:12 AM
This discussion has been copied to a work item. Click here to go to the work item.