最终还是决定直接以源代码方式发布GacUI了

最终还是决定直接以源代码方式发布GacUI了
    在制作GacUI读pdb生成代码的过程中,感受到了C++语言设计和dll的需求之间的鸿沟。对于一个充分利用了C++各种功能的类库来说,制作成dll具有非常大的困难,特别是在函数返回POD(Plain Old Data)的引用,和输入输出带有泛型的类上面。所以现在还是决定以源代码的方式来发布GacUI。但是pdb生成代码并没有白做,因为反射还是存在的。但是因为GacUI一共有48000行代码,80多个源代码文件,直接发布使用起来总是不方便。所以我写了个小工具,根据xml的配置来将源代码合并成少数几个比较大的代码文件。这样使用的时候,只需要直接把几个cpp拖进工程里面,就可以使用了。而且根据 之前发布的一个投票,似乎大家也最喜欢这种方法。因此这次的决定,仅仅删掉了作为backup plan的dll方法。

    这里我给出小工具的代码和配置文件。这个配置文件是基于GacUI做出来的,不过大家可以修改它,以便用于自己的工程上面:
<? xml version="1.0" encoding="utf-8"  ?>
< codegen >
  
< projects >
    
< project  path ="..\..\..\GacUISrc\GacUISrc.vcxproj"   />
  
</ projects >
  
< categories >
    
< category  name ="vlpp"  pattern ="\Library\" />
    
< category  name ="gacui"  pattern ="\GacUILibrary\" >
      
< except  filename ="GacUI_WinMain.cpp"   />
      
< except  filename ="GuiTypeDescriptorImpHelper.cpp"   />
      
< except  filename ="GuiTypeDescriptorImpProvider_codegen.cpp"   />
    
</ category >
  
</ categories >
  
< output  path ="..\..\..\GacUILibraryExternal\" >
    
< codepair  category ="vlpp"  filename ="Vlpp"   />
    
< codepair  category ="gacui"  filename ="GacUI"   />
    
< header  source ="..\..\..\GacUILibrary\GacUI.h"  filename ="GacUIIncludes"   />
  
</ output >
</ codegen >

    在这里面,project包含了用于开发这个工程的所有VC++2010的工程文件的地址,然后使用category对他们进行分类(pattern是文件全名的某个部分),最后对每一个部分生成一对cpp和h。在最后生成代码对的时候,如果源代码从一开始就存在依赖关系的话,那么在代码对的h文件里面,会包含依赖的代码对的h。在这里,vlpp是独立的,而gacui依赖了vlpp,所以gacui.h将会#include"vlpp.h",而cpp只include自己的h文件。output里面除了codepair以外还有header,header是不参与codepair计算的,纯粹为了生成“可以省事直接include”的头文件。在这个例子里面,GacUIIncludes.h将会包含完整的GacUI.h和一部分的Vlpp.h,而且是可以通过编译的。

 驱动器 E 中的卷没有标签。
 卷的序列号是 
9614 - 79B9

 E:\Codeplex\vlpp\Workspace\Tools\Release\SideProjects\GacUISrc\GacUILibraryExternal 的目录

2012 / 02 / 29    21 : 42      < DIR >           .
2012 / 02 / 29    21 : 42      < DIR >           ..
2012 / 02 / 29    21 : 42                   0  dir.txt
2012 / 02 / 29    21 : 18             677 , 987  GacUI.cpp
2012 / 02 / 29    21 : 18             304 , 231  GacUI.h
2012 / 02 / 29    21 : 18             481 , 551  GacUIIncludes.h
2012 / 02 / 29    21 : 18              69 , 348  Vlpp.cpp
2012 / 02 / 29    21 : 18             310 , 126  Vlpp.h
               
6  个文件       1 , 843 , 243  字节
               
2  个目录  166 , 357 , 680 , 128  可用字节

   在生成的时候,生成器将会阅读代码本身,然后获取#include "path",然后对他们的关系进行处理。这个工具的代码如下:
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.IO;
using  System.Xml.Linq;
using  System.Text.RegularExpressions;

namespace  Codegen
{
    
class  Program
    {
        
static   string [] GetCppFiles( string  projectFile)
        {
            
string  np  =   @" http://schemas.microsoft.com/developer/msbuild/2003 " ;
            XDocument document 
=  XDocument.Load(projectFile);
            
return  document
                .Root
                .Elements(XName.Get(
" ItemGroup " , np))
                .SelectMany(e 
=>  e.Elements(XName.Get( " ClCompile " , np)))
                .Select(e 
=>  Path.GetFullPath(Path.GetDirectoryName(projectFile)  +   " \\ "   +  e.Attribute( " Include " ).Value))
                .ToArray();
        }

        
static  Dictionary < string string [] >  CategorizeCodeFiles(XDocument config,  string [] files)
        {
            Dictionary
< string string [] >  categorizedFiles  =   new  Dictionary < string string [] > ();
            
foreach  (var e  in  config.Root.Element( " categories " ).Elements( " category " ))
            {
                
string  name  =  e.Attribute( " name " ).Value;
                
string  pattern  =  e.Attribute( " pattern " ).Value.ToUpper();
                
string [] exceptions  =  e.Elements( " except " ).Select(x  =>  x.Attribute( " filename " ).Value.ToUpper()).ToArray();
                categorizedFiles.Add(
                    name,
                    files
                        .Where(f 
=>  f.ToUpper().Contains(pattern))
                        .Where(f 
=>   ! exceptions.Contains(Path.GetFileName(f).ToUpper()))
                        .ToArray()
                        );
            }
            
foreach  (var a  in  categorizedFiles.Keys)
            {
                
foreach  (var b  in  categorizedFiles.Keys)
                {
                    
if  (a  !=  b)
                    {
                        
if  (categorizedFiles[a].Intersect(categorizedFiles[b]).Count()  !=   0 )
                        {
                            
throw   new  ArgumentException();
                        }
                    }
                }
            }
            
return  categorizedFiles;
        }

        
static  Dictionary < string string [] >  ScannedFiles  =   new  Dictionary < string string [] > ();
        
static  Regex IncludeRegex  =   new  Regex( @" ^\s*\#include\s*""(?<path>[^""]+)""\s*$ " );
        
static  Regex IncludeSystemRegex  =   new  Regex( @" ^\s*\#include\s*\<(?<path>[^""]+)\>\s*$ " );

        
static   string [] GetIncludedFiles( string  codeFile)
        {
            codeFile 
=  Path.GetFullPath(codeFile).ToUpper();
            
string [] result  =   null ;
            
if  ( ! ScannedFiles.TryGetValue(codeFile,  out  result))
            {
                List
< string >  directIncludeFiles  =   new  List < string > ();
                
foreach  (var line  in  File.ReadAllLines(codeFile))
                {
                    Match match 
=  IncludeRegex.Match(line);
                    
if  (match.Success)
                    {
                        
string  path  =  match.Groups[ " path " ].Value;
                        path 
=  Path.GetFullPath(Path.GetDirectoryName(codeFile)  +   @" \ "   +  path).ToUpper();
                        
if  ( ! directIncludeFiles.Contains(path))
                        {
                            directIncludeFiles.Add(path);
                        }
                    }
                }

                
for  ( int  i  =  directIncludeFiles.Count  -   1 ; i  >=   0 ; i -- )
                {
                    directIncludeFiles.InsertRange(i, GetIncludedFiles(directIncludeFiles[i]));
                }
                result 
=  directIncludeFiles.Distinct().ToArray();
                ScannedFiles.Add(codeFile, result);
            }
            
return  result;
        }

        
static   string [] SortDependecies(Dictionary < string string [] >  dependeicies)
        {
            var dep 
=  dependeicies.ToDictionary(p  =>  p.Key, p  =>   new  HashSet < string > (p.Value));
            List
< string >  sorted  =   new  List < string > ();
            
while  (dep.Count  >   0 )
            {
                
bool  found  =   false ;
                
foreach  (var p  in  dep)
                {
                    
if  (p.Value.Count  ==   0 )
                    {
                        found 
=   true ;
                        sorted.Add(p.Key);
                        
foreach  (var q  in  dep.Values)
                        {
                            q.Remove(p.Key);
                        }
                        dep.Remove(p.Key);
                        
break ;
                    }
                }
                
if  ( ! found)
                {
                    
throw   new  ArgumentException();
                }
            }
            
return  sorted.ToArray();
        }

        
static   void  Combine( string [] files,  string  outputFilename, HashSet < string >  systemIncludes,  params   string [] externalIncludes)
        {
            
try
            {
                
using  (StreamWriter writer  =   new  StreamWriter( new  FileStream(outputFilename, FileMode.Create), Encoding.Default))
                {
                    writer.WriteLine(
" /*********************************************************************** " );
                    writer.WriteLine(
" THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY " );
                    writer.WriteLine(
" DEVELOPER: 陈梓瀚(vczh) " );
                    writer.WriteLine(
" ***********************************************************************/ " );
                    
foreach  (var inc  in  externalIncludes)
                    {
                        writer.WriteLine(
" #include \ " { 0 }\ "" , inc);
                    }

                    
foreach  (var file  in  files)
                    {
                        writer.WriteLine(
"" );
                        writer.WriteLine(
" /*********************************************************************** " );
                        writer.WriteLine(file);
                        writer.WriteLine(
" ***********************************************************************/ " );
                        
foreach  (var line  in  File.ReadAllLines(file, Encoding.Default))
                        {
                            Match match 
=   null ;

                            match 
=  IncludeSystemRegex.Match(line);
                            
if  (match.Success)
                            {
                                
if  (systemIncludes.Add(match.Groups[ " path " ].Value.ToUpper()))
                                {
                                    writer.WriteLine(line);
                                }
                            }
                            
else
                            {
                                match 
=  IncludeRegex.Match(line);
                                
if  ( ! match.Success)
                                {
                                    writer.WriteLine(line);
                                }
                            }
                        }
                    }
                }
                Console.WriteLine(
" Succeeded to write: {0} " , outputFilename);
            }
            
catch  (Exception)
            {
                Console.WriteLine(
" Failed to write: {0} " , outputFilename);
            }
        }

        
static   void  Combine( string  inputFilename,  string  outputFilename,  params   string [] externalIncludes)
        {
            HashSet
< string >  systemIncludes  =   new  HashSet < string > ();
            
string [] files  =  GetIncludedFiles(inputFilename).Concat( new   string [] { inputFilename }).Distinct().ToArray();
            Combine(files, outputFilename, systemIncludes, externalIncludes);
        }

        
static   void  Main( string [] args)
        {
            
//  load configuration
            XDocument config  =  XDocument.Load( " CodegenConfig.xml " );
            
string  folder  =  Path.GetDirectoryName( typeof (Program).Assembly.Location)  +   " \\ " ;

            
//  collect project files
             string [] projectFiles  =  config.Root
                .Element(
" projects " )
                .Elements(
" project " )
                .Select(e 
=>  Path.GetFullPath(folder  +  e.Attribute( " path " ).Value))
                .ToArray();

            
//  collect code files
             string [] unprocessedCppFiles  =  projectFiles.SelectMany(GetCppFiles).Distinct().ToArray();
            
string [] unprocessedHeaderFiles  =  unprocessedCppFiles.SelectMany(GetIncludedFiles).Distinct().ToArray();

            
//  categorize code files
            var categorizedCppFiles  =  CategorizeCodeFiles(config, unprocessedCppFiles);
            var categorizedHeaderFiles 
=  CategorizeCodeFiles(config, unprocessedHeaderFiles);
            var outputFolder 
=  Path.GetFullPath(folder  +  config.Root.Element( " output " ).Attribute( " path " ).Value);
            var categorizedOutput 
=  config.Root
                .Element(
" output " )
                .Elements(
" codepair " )
                .ToDictionary(
                    e 
=>  e.Attribute( " category " ).Value,
                    e 
=>  Path.GetFullPath(outputFolder  +  e.Attribute( " filename " ).Value
                    ));

            
//  calculate category dependencies
            var categoryDependencies  =  categorizedCppFiles
                .Keys
                .Select(k 
=>
                    {
                        var headerFiles 
=  categorizedCppFiles[k]
                            .SelectMany(GetIncludedFiles)
                            .Distinct()
                            .ToArray();
                        var keys 
=  categorizedHeaderFiles
                            .Where(p 
=>  p.Value.Any(h  =>  headerFiles.Contains(h)))
                            .Select(p 
=>  p.Key)
                            .Except(
new   string [] { k })
                            .ToArray();
                        
return  Tuple.Create(k, keys);
                    })
                .ToDictionary(t 
=>  t.Item1, t  =>  t.Item2);

            
//  sort categories by dependencies
            var categoryOrder  =  SortDependecies(categoryDependencies);
            Dictionary
< string , HashSet < string >>  categorizedSystemIncludes  =   new  Dictionary < string , HashSet < string >> ();

            
//  generate code pair header files
             foreach  (var c  in  categoryOrder)
            {
                
string  output  =  categorizedOutput[c]  +   " .h " ;
                List
< string >  includes  =   new  List < string > ();
                
foreach  (var dep  in  categoryDependencies[c])
                {
                    includes.AddRange(categorizedSystemIncludes[dep]);
                }
                HashSet
< string >  systemIncludes  =   new  HashSet < string > (includes.Distinct());
                categorizedSystemIncludes.Add(c, systemIncludes);
                Combine(
                    categorizedHeaderFiles[c],
                    output,
                    systemIncludes,
                    categoryDependencies[c]
                        .Select(d 
=>  Path.GetFileName(categorizedOutput[d]  +   " .h " ))
                        .ToArray()
                    );
            }

            
//  generate code pair cpp files
             foreach  (var c  in  categoryOrder)
            {
                
string  output  =  categorizedOutput[c];
                
string  outputHeader  =  Path.GetFileName(output  +   " .h " );
                
string  outputCpp  =  output  +   " .cpp " ;
                HashSet
< string >  systemIncludes  =  categorizedSystemIncludes[c];
                Combine(
                    categorizedCppFiles[c],
                    outputCpp,
                    systemIncludes,
                    outputHeader
                    );
            }

            
//  generate header files
            var headerOutput  =  config.Root
                .Element(
" output " )
                .Elements(
" header " )
                .ToDictionary(
                    e 
=>  Path.GetFullPath(folder  +  e.Attribute( " source " ).Value),
                    e 
=>  Path.GetFullPath(outputFolder  +  e.Attribute( " filename " ).Value)
                    );
            
foreach  (var o  in  headerOutput)
            {
                Combine(o.Key, o.Value 
+   " .h " );
            }
        }
    }
}

    代码已经checkin在了 Vczh Library++3.0(Tools\Release\SideProjects\GacUISrc\GacUISrc.sln)下面,里面也包含了生成后的代码。

你可能感兴趣的:(最终还是决定直接以源代码方式发布GacUI了)