[.NET] VB (en C#) project compileren - OF: VB/C# code parsen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Hey,

Ik ben een programmaatje aan het schrijven waarin men VB en C# source code bestanden kan openen. Het is een soort van 'lightweight' visual studio IDE zonder de meeste 'zware' dingen die de IDE langzaam maken met opstarten en dergelijke. De bedoeling is dat men snel even een source file kan bekijken of kan aanpassen zonder de hele IDE te hoeven openen (of zelfs geinstalleerd te hoeven hebben). Ik zelf maak vaak genoeg mee dat ik een source file even snel in notepad (of notepad++ nog beter natuurlijk) open in plaats van dat ik de moeite neem om de IDE te starten.

Om een lang verhaal enigzins kort te houden: toch wil ik graag een kleinschalig compilen of parsen van de code inbouwen. Het idee uiteindelijk is dat men een solution file, vb project of C# project kunnen openen (dit werkt al), waarna men niet alleen de files krijgt in een 'solution explorer' (gewoon een treeview met alle files) maar veel belangrijker: ook een soort van Intellisense support (voor zover mogelijk) en een 'class view' window, met een treeview met de types en hun members.

Ik heb een tijd lang gezocht naar manieren om VB en C# code te parsen (daarmee bedoel ik: code inlezen en bepalen welke types er gedeclareerd zijn en welke members ze hebben, etc, zodat ik een class view kan opbouwen), maar dat lijkt niet op een handige manier mogelijk te zijn behalve handmatig een parser te schrijven. Dat heb ik nog nooit gedaan dus dat zal echt een 'last resort' zijn. Toch: als er hier goeie documentatie over bestaan zou ik dat graag weten. Ik kwam een tijd geleden toevallig een document (vast op MSDN) tegen waar de VB en C# talen exact uitgelegd werden, welk format ze hadden en wanneer welke 'token' geldig was, zo'n soort dingen. Maar die kan ik helaas niet meer vinden...


Anyway, ik ben dus op zoek geweest naar slimme manieren om dit op te lossen, of nog beter: het door .NET zelf te laten doen. In de System.CodeDom(.Compiler) namespaces lijken een groot aantal classes te zijn die met compilen en parsen te maken hebben. Maar voor zover ik kan zien is er steeds maar een method (CodeParser.Parse) die aan parsen (in tegenstelling tot compilen) doet, en die is bij geen enkele class geimplementeerd...

Toen bedacht ik echter een 'omweg' die zeker weten mogelijk is, maar misschien niet ideaal is. Ik zou natuurlijk de source code kunnen compilen (System.CodeDom.Compiler.CodeProvider.CompileAssemblyFromSource) en daarna met Reflection de assembly terug uit kunnen lezen. Daarmee krijg ik alle types en hun members 'kado'. Dat is natuurlijk niet ideaal: voor het compilen zal vast en zeker ook een soort van parser zijn werk doen dus ben ik alles eigenlijk dubbel aan het doen, maar behalve alles handmatig te doen lijkt dit de enige optie te zijn.

Anyway, dat is dus wat ik nu aan het proberen ben. Als men een project file in laadt dan sla ik een aantal dingen op (die lees ik uit de project xml file) namelijk:
- De files in het project
- De References (referenced assemblies) in het project
- De Imports (in het geval van VB kunnen er "impliciet" geimporteerde namespaces zijn die niet in de source code voorkomen).

Ik probeer nu de source files in dit project te compileren, maar voor de meeste projecten gaat het hopeloos mis... Dit is de code die ik gebruik:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    Private Shared Function CompileSource(ByVal language As Languages, _
                                          ByVal sources As IEnumerable(Of String), _
                                          ByVal references As IEnumerable(Of String)) As Assembly

        ' Get the CodeDomProvider used to compile
        Dim provider = GetCodeDomProvider(language)

        ' Set up some parameters
        Dim params As New CompilerParameters()
        params.GenerateInMemory = True
        params.GenerateExecutable = False

        ' Add the referenced assemblies found in the project xml file
        params.ReferencedAssemblies.AddRange(references.ToArray())

        ' Finally compile the sources and return the compiled assembly
        Dim result = provider.CompileAssemblyFromSource(params, sources.ToArray())
        Return result.CompiledAssembly
    End Function

Voor de meest simpele C# projecten gaat dit goed. Ik krijg nu een assembly die ik met Reflection kan inspecteren en daaruit kan ik een mooie treeview opbouwen.

Maar bij elk VB project gaat het mis. Ik heb eigenlijk twee problemen:
1. Ik weet niet hoe ik de 'impliciete' imports moet behandelen, en
2. Ik krijg errors die ik nooit eerder heb gezien, die niet in een C# project voorkomen en ik heb geen idee hoe ik ze oplos.


Voor puntje 1:
In de XML file kan ik een aantal imports statements uitlezen die in VB altijd 'verborgen' zijn. Ze staan niet fysiek in de source code maar bij de compilatie worden ze wel gebruikt. Een voorbeeld is de System.Drawing namespace, die in een winforms project automatisch geimporteerd wordt. Je kan daarom altijd een Color of een Rectangle oid gebruiken zonder 'Imports System.Drawing' boven de code te zetten. Maar als ik deze code nu laat compileren met mijn code dan werkt het natuurlijk niet: deze compiler heeft geen idee van deze verborgen geimporteerde namespaces dus kan hij Color, Rectangle, etc niet vinden.

Ik heb nu wel de imports statements die ik boven de code moet zetten, dus ik zou het in principe handmatig kunnen doen door de source steeds handmatig aan te passen, maar ik zit nu met twee problemen:
- De imports staan niet altijd bovenaan. Als er een 'Option' statement is dan moeten ze daar nog onder,
- Je mag niet dezelfde import meerdere keren gebruiken, dan gaat het ook mis.
Dus ik moet de source weer zelf gaan lezen om te kijken welke imports statements er al zijn, en waar de 'hidden' imports toegevoegd moeten worden. Dat is niet echt de bedoeling want dat is precies wat ik probeer te vermijden... Kan dit niet op een betere manier?


Voor puntje 2, dit zijn twee voorbeelden van de errors die ik krijg:
Type 'HatchBrushTest.My.MySettings' is not defined.
'HatchBrushTest' is not a member of '<Default>'.
'HatchBrushTest' is de naam van het project (en dus ook de default namespace). Ik denk dat daar het probleem zit: met de default namespace. Weer iets wat wel in VB zit maar niet in C#. Maar ik heb geen idee hoe ik dit moet oplossen...? Moet ik alle source code in "Namespace <project naam>" tags gaan wikkelen? Dan moet ik dus alweer zelf de source gaan lezen, en alweer: dat doe ik liever niet als het niet anders kan...


Dus, wat ik eigenlijk vraag is: hoe compile je zonder problemen een heel project? Ik heb uiteraard zitten googlen en op MSDN gekeken, maar eigenlijk vind ik alleen maar voorbeelden om kleine 'scriptjes' te compilen, zoals een mathematische expressie die men dan dynamisch kan intypen ofzo. Dat werkt allemaal prima, maar zodra ik een heel project ga compilen kom ik dus deze problemen tegen...
Er moet hier toch wel meer en betere informatie over te vinden zijn? Vooral over deze 'implicit imports' in VB: dat komt iedereen toch tegen die een vb project wil compilen in code? Toch kan ik niets vinden... Misschien zoek ik met de verkeerde keywords.


Weet iemand hier iets meer van? Bedankt!

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

Verwijderd

Is compilen met MSBuild.exe een oplossing? En daarna die assembly laden met reflectie en dan de methodes, variabelen en dergelijke eruit halen?

Acties:
  • 0 Henk 'm!

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

MSBuild en reflection zal de makkelijkste weg zijn.
Een heel project compilen vergt namelijk nogal wat logica en dat gaat de CodeDOM provider een beetje te boven zonder heel veel zelf te gaan klussen vrees ik.

Ah ik zie net in de MSDN documentatie dat je in de CodeDomProvider een method CompileFromSource hebt die een array van filenames accepteert. Denk dat je daarmee wel een eind kunt komen.

[ Voor 31% gewijzigd door MTWZZ op 06-09-2010 20:54 ]

Nu met Land Rover Series 3 en Defender 90


Acties:
  • 0 Henk 'm!

Verwijderd

MTWZZ schreef op maandag 06 september 2010 @ 20:35:
Ah ik zie net in de MSDN documentatie dat je in de CodeDomProvider een method CompileFromSource hebt die een array van filenames accepteert. Denk dat je daarmee wel een eind kunt komen.
Dat doet die toch al? tenminste, ik neem aan dat daar die sources.ToArray() voor is :P die in
provider.CompileAssemblyFromSource(params, sources.ToArray())
staat.

[ Voor 9% gewijzigd door Verwijderd op 06-09-2010 21:02 ]


Acties:
  • 0 Henk 'm!

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Verwijderd schreef op maandag 06 september 2010 @ 21:02:
[...]


Dat doet die toch al? tenminste, ik neem aan dat daar die sources.ToArray() voor is :P die in
provider.CompileAssemblyFromSource(params, sources.ToArray())
staat.
damn :X
Nou ja laten we het er maar op houden dat ik vandaag al te veel code heb gezien :+

Nu met Land Rover Series 3 en Defender 90


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Ja, dat doe ik inderdaad al. Ik geef gewoon alle files in het project mee, en ik neem aan dat hij dan zelf wel uitzoekt welke files hij eerst moet compilen (stell dat Class1 een instance van Class2 gebruikt dan zal hij toch eerst Class2 moeten kennen voordat hij Class1 kan compilen). Misschien doet hij zelfs dat niet... Zover ben ik namelijk nog niet gekomen want hij krijgt nog niet een enkele file gecompiled :)

MSBuild... Dat is de compiler van visual studio zeg maar, toch? Moet de gebruiker daar Visual Studio (of Express) voor geintalleerd hebben, of kan ik die ook gewoon meeleveren? Ik neem aan dat ik die gewoon als command line kan aanroepen? Dat zal ik dan maar proberen...

Maar dan heb ik een tweede probleem: ik neem even aan dat deze MSBuild gewoon een solution of een project build, en niet de afzonderlijke files (toch? Hij zal toch ook informatie over het project moeten hebben, welke framework versie etc?)
Maar dat was ik eigenlijk niet van plan, ik wilde graag de afzonderlijke files compilen. Dan kan de gebruiker namelijk een project openen (wat in mijn geval niet veel meer inhoudt dan gewoon alle source files openen), en als hij dan een of meerdere source files aanpast (bijv een file met een class toevoegen) dan kan ik die files meteen weer compileren en zal die nieuwe class ook in de class view meteen zichtbaar zijn. Dat gaat niet werken als ik die files niet weer zelf terugschrijf naar de project file, en dat was ik eigenlijk niet van plan. Ik denk dat dat namelijk heel wat meer moeite vergt... Ik zal het proberen, maar ik ga liever niet zelf project files wegschrijven. Maar goed, als het niet anders kan...

Bedankt in ieder geval :)
Ik wacht nog even op wat meer reacties.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

Verwijderd

MSBuild kan csproj en sln files als ik het me goed herinner. Ik denk inderdaad niet dat hij afzonderlijke files kan, maar eigenlijk wil je dat ook niet (altijd).

Omdat je dus ook objecten in een andere class gedefinieerd kunt hebben en dan zou die die ook altijd nodig hebben. Dus afzonderlijke files compilen kan eigenlijk vrijwel nooit.

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Nee maar je kan natuurlijk wel meerdere files tegenlijk compilen, en dat zou die CodeDom compiler ook moeten kunnen.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

msbuild is gewoon onderdeel van de .NET 1.x, 2.0 en 4.0 runtimes en kun je dus gewoon hergebruiken. Maar waarom heb je bijvoorbeeld niet eerst naar bestaanden alternatieven zoals Sharp Develop gekeken. Deze kan niet alleen overweg met msbuild, maar ook met nant build scripts. De opties welke jij wilt programmeren in je eigen IDE zitten daar gewoon in.

Het lijkt mij zeer onverstandig om zonder msbuild projecten te compileren. De gehele configuratie van je solution en projecten zijn als properties van de msbuild definitie.

Daarnaast ontgaat mij het nut van bestanden parallel compileren. Je compileert namelijk een assembly en de basis voor die assembly wordt gevormd door een of meerdere source bestanden. Indien mogelijk zal msbuild assemblies (projecten in VS) parallel compileren, maar vrijwel altijd heeft het ene project een dependency op een ander project in de solution.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 13-09 17:54
En kijk ook eens naar Snippet Compiler. Die doet namelijk al redelijk wat je wilt.

[ Voor 9% gewijzigd door creator1988 op 07-09-2010 09:51 ]

Pagina: 1