#region Apache License, Version 2.0
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Build.BuildEngine;
using NMaven.Artifact;
using NMaven.Solution;
using NMaven.Model.Pom;
namespace NMaven.Solution.Impl
{
///
/// Implementation of the IProjectGenerator.
///
internal sealed class ProjectGeneratorImpl : IProjectGenerator
{
///
/// Constructor
///
internal ProjectGeneratorImpl()
{
}
public IProjectReference GenerateProjectFor(NMaven.Model.Pom.Model model,
DirectoryInfo sourceFileDirectory,
String projectFileName,
ICollection projectReferences,
DirectoryInfo localRepository)
{
Guid projectGuid = Guid.NewGuid();
if (projectReferences == null)
{
projectReferences = new List();
}
Project project = GetProjectFromPomModel(model,
sourceFileDirectory,
projectFileName,
projectGuid,
@"..\..\..\target\bin\Debug\",
@"..\..\..\target\obj\",
projectReferences,
localRepository);
FileInfo fileInfo = new FileInfo(sourceFileDirectory.FullName + @"\" + projectFileName + ".csproj");
project.Save(fileInfo.FullName);
IProjectReference projectReference = Factory.createDefaultProjectReference();
projectReference.CSProjectFile = fileInfo;
projectReference.ProjectGuid = projectGuid;
projectReference.ProjectName = projectFileName;
return projectReference;
}
public void GenerateSolutionFor(FileInfo fileInfo, ICollection projectReferences)
{
TextWriter writer =
new StreamWriter(fileInfo.FullName, false, System.Text.Encoding.UTF8);
writer.WriteLine("");
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 9.00");
writer.WriteLine("# Visual Studio 2005");
writer.WriteLine("# SharpDevelop 2.1.0.2376");
Guid solutionGuid = Guid.NewGuid();
foreach(IProjectReference projectReference in projectReferences)
{
writer.Write("Project(\"{");
writer.Write("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC");
writer.Write("}\") = \"");
writer.Write(projectReference.ProjectName);
writer.Write("\", \"");
writer.Write(projectReference.CSProjectFile.FullName);
writer.Write("\", \"{");
writer.Write(projectReference.ProjectGuid.ToString());
writer.WriteLine("}\"");
writer.WriteLine("EndProject");
}
writer.Flush();
writer.Close();
Console.WriteLine("NMAVEN-000-000: Generate solution file: File Name = " + fileInfo.FullName);
}
public NMaven.Model.Pom.Model CreatePomModelFor(String fileName)
{
TextReader reader = new StreamReader(fileName);
XmlSerializer serializer = new XmlSerializer(typeof(NMaven.Model.Pom.Model));
return (NMaven.Model.Pom.Model) serializer.Deserialize(reader);
}
///
/// Returns a project binding (xmlns="http://schemas.microsoft.com/developer/msbuild/2003") from the given model
/// (pom.xml) file
///
/// the model binding for a pom.xml file
/// the directory containing the source files
/// the name of the assembly: often corresponds to the artifact id from the pom
/// the GUID of the project
/// directory where the IDE output files are placed
/// directory where the IDE output files are placed
/// references to other projects that this project is dependent upon
/// Returns a project binding for the specified model
private Project GetProjectFromPomModel(NMaven.Model.Pom.Model model,
DirectoryInfo sourceFileDirectory,
String assemblyName,
Guid projectGuid,
String assemblyOutputPath,
String baseIntermediateOutputPath,
ICollection projectReferences,
DirectoryInfo localRepository)
{
if(model == null || sourceFileDirectory == null)
{
throw new ExecutionException("NMAVEN-000-000: Missing required parameter.");
}
Engine engine = new Engine(Environment.GetEnvironmentVariable("SystemRoot") + @"\Microsoft.NET\Framework\v2.0.50727");
Project project = new Project(engine);
Console.WriteLine("ProjectGuid = " + projectGuid.ToString() + ", RootNameSpace = " +
model.groupId + ", AssemblyName = " + assemblyName + ", BaseIntPath = " +
baseIntermediateOutputPath + ", OutputType = " + GetOutputType(model.packaging) +
", Packaging = " + model.packaging);
//Main Properties
BuildPropertyGroup groupProject = project.AddNewPropertyGroup(false);
groupProject.AddNewProperty("ProjectGuid", "{" + projectGuid.ToString() + "}");
BuildProperty buildProperty = groupProject.AddNewProperty("Configuration", "Debug");
buildProperty.Condition = " '$(Configuration)' == '' ";
groupProject.AddNewProperty("RootNameSpace", model.groupId);
groupProject.AddNewProperty("AssemblyName", assemblyName);
groupProject.AddNewProperty("BaseIntermediateOutputPath", baseIntermediateOutputPath);
groupProject.AddNewProperty("OutputType", GetOutputType(model.packaging));
//Debug Properties
groupProject = project.AddNewPropertyGroup(false);
buildProperty.Condition = " '$(Configuration)' == '' ";
groupProject.AddNewProperty( "OutputPath", assemblyOutputPath, false);
project.AddNewImport(@"$(MSBuildBinPath)\Microsoft.CSharp.Targets", null);
DirectoryInfo configDirectory = new DirectoryInfo(Environment.CurrentDirectory + @"\src\main\config");
if(configDirectory.Exists)
{
BuildItemGroup configGroup = project.AddNewItemGroup();
foreach(FileInfo fileInfo in configDirectory.GetFiles())
{
if(fileInfo.Extension.Equals("exe.config"))
{
configGroup.AddNewItem("None", @"src\main\config\" + fileInfo.Name);
}
}
}
AddProjectDependencies(project, model, sourceFileDirectory, localRepository);
AddFoldersToProject(project, null, sourceFileDirectory, sourceFileDirectory);
AddClassFilesToProject(project, null, sourceFileDirectory, sourceFileDirectory);
AddProjectReferences(project, assemblyName, projectReferences);
return project;
}
private void AddProjectReferences(Project project, String projectName, ICollection projectReferences)
{
BuildItemGroup itemGroup = project.AddNewItemGroup();
foreach(IProjectReference projectReference in projectReferences)
{
BuildItem buildItem = itemGroup.AddNewItem("ProjectReference", projectReference.CSProjectFile.FullName);
buildItem.SetMetadata("Project", "{" + projectReference.ProjectGuid.ToString() + "}");
buildItem.SetMetadata("Name", projectName);
}
}
private void AddFoldersToProject(Project project, BuildItemGroup folderGroup, DirectoryInfo rootDirectory,
DirectoryInfo sourceFileDirectory)
{
DirectoryInfo[] directoryInfos = rootDirectory.GetDirectories();
if(directoryInfos != null && directoryInfos.Length > 0)
{
if(folderGroup == null) folderGroup = project.AddNewItemGroup();
foreach(DirectoryInfo di in directoryInfos)
{
if(di.FullName.Contains(".svn") || di.FullName.Contains(@"obj") || di.FullName.Contains(@"bin"))
continue;
folderGroup.AddNewItem("Folder", di.FullName.Substring(sourceFileDirectory.FullName.Length));
AddFoldersToProject(project, folderGroup, di, sourceFileDirectory);
}
}
}
private void AddClassFilesToProject(Project project, BuildItemGroup compileGroup, DirectoryInfo rootDirectory,
DirectoryInfo sourceFileDirectory)
{
DirectoryInfo[] directoryInfos = rootDirectory.GetDirectories();
if(directoryInfos != null && directoryInfos.Length > 0)
{
if (compileGroup == null)
{
compileGroup = project.AddNewItemGroup();
}
foreach(DirectoryInfo di in directoryInfos)
{
if (di.FullName.Contains(".svn") || di.FullName.Contains("obj") || di.FullName.Contains("bin"))
{
continue;
}
foreach(FileInfo fileInfo in di.GetFiles())
{
BuildItem buildItem =
compileGroup.AddNewItem("Compile",
fileInfo.FullName.Substring(sourceFileDirectory.FullName.Length));
}
AddClassFilesToProject(project, compileGroup, di, sourceFileDirectory);
}
}
}
private void AddProjectDependencies(Project project, NMaven.Model.Pom.Model model, DirectoryInfo sourceFileDirectory,
DirectoryInfo localRepository)
{
BuildItemGroup group = project.AddNewItemGroup();
group.AddNewItem("Reference", "System.Xml");
if(model.dependencies != null)
{
ArtifactContext artifactContext = new ArtifactContext();
foreach(Dependency dependency in model.dependencies)
{
//String artifactExtension = (dependency.type == "module") ? "dll" : GetExtension(dependency.type);
NMaven.Artifact.Artifact dependencyArtifact = artifactContext.CreateArtifact(dependency.groupId,
dependency.artifactId, dependency.version, dependency.type);
String repoPath = PathUtil.GetUserAssemblyCacheFileFor(dependencyArtifact, localRepository).FullName;
BuildItem buildItem = group.AddNewItem("Reference", dependency.artifactId);
//TODO: Fix this. Just because it is in the GAC on the system that builds the .csproj does not mean
//it is in the GAC on another system.
if (!dependency.GetType().Equals("gac") && !IsInGac(dependency.artifactId))
{
buildItem.SetMetadata("HintPath", repoPath, false);
}
}
}
DirectoryInfo[] directoryInfos = sourceFileDirectory.GetDirectories();
ClassParser classParser = new ClassParser();
List fileInfos = new List();
AddFileInfosFromSourceDirectories(sourceFileDirectory, fileInfos);
List dependencies = classParser.GetDependencies(fileInfos);
foreach(String dependency in dependencies)
{
try {
String assembly = GetAssemblyFor(dependency);
if(IsInGac(assembly)) {
group.AddNewItem("Reference", assembly);
}
}
catch(Exception e)
{
Console.WriteLine("NMAVEN-000-000: Could not find assembly dependency", e.Message);
}
}
}
private bool IsInGac(String assembly)
{
return new DirectoryInfo(Environment.GetEnvironmentVariable("SystemRoot")
+ @"\assembly\GAC_MSIL\" + assembly).Exists;
}
private String GetAssemblyFor(String dependency)
{
return (dependency.Trim().Equals("System.Resources")) ? "System.Windows.Forms" : dependency;
}
private void AddFileInfosFromSourceDirectories(DirectoryInfo sourceFileDirectory, List fileInfos )
{
DirectoryInfo[] directoryInfos = sourceFileDirectory.GetDirectories();
if(directoryInfos != null && directoryInfos.Length > 0)
{
foreach(DirectoryInfo di in directoryInfos)
{
if (di.FullName.Contains(".svn") || di.FullName.Contains("obj") || di.FullName.Contains("bin"))
{
continue;
}
fileInfos.AddRange(di.GetFiles());
AddFileInfosFromSourceDirectories(di, fileInfos);
}
}
}
private String GetOutputType(String type)
{
if (type.Equals("library") || type.Equals("netplugin") || type.Equals("visual-studio-addin")
|| type.Equals("sharp-develop-addin") || type.Equals("nar")) return "Library";
else if (type.Equals("exe")) return "Exe";
else if (type.Equals("winexe")) return "WinExe";
else if (type.Equals("module")) return "Module";
return null;
}
private String GetExtension(String type)
{
if (type.Equals("library") || type.Equals("netplugin") ) return "dll";
else if (type.Equals("exe")) return "exe";
else if (type.Equals("winexe")) return "exe";
else if (type.Equals("module")) return "netmodule";
return null;
}
private class ClassParser {
public List GetDependencies(List fileInfos)
{
List dependencies = new List();
foreach(FileInfo fileInfo in fileInfos)
{
try
{
using (StreamReader sr = new StreamReader(fileInfo.FullName))
{
String line;
while ((line = sr.ReadLine()) != null)
{
if (line.StartsWith("namespace")) break;
if (line.StartsWith("//")) continue;
if (line.StartsWith("using")) {
String[] tokens = line.Remove(line.Length - 1).Split(new char[1]{' '});
if(!dependencies.Contains(tokens[1]))
{
dependencies.Add(tokens[1]);
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
return dependencies;
}
}
}
}