diff --git a/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.Designer.prg b/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.Designer.prg index a584dcd2f4..e7089f6ec8 100644 --- a/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.Designer.prg +++ b/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.Designer.prg @@ -1,4 +1,15 @@ -BEGIN NAMESPACE VFPXPorter +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime version: 4.0.30319.42000 +// Generator : XSharp.CodeDomProvider 2.24.0.1 +// Timestamp : 28/12/2025 12:51:12 +// +// Changes to this file may cause incorrect behavior and may be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +BEGIN NAMESPACE VFPXPorter PUBLIC PARTIAL CLASS ExportProjectWindow ; INHERIT System.Windows.Forms.Form PRIVATE components := NULL AS System.ComponentModel.IContainer @@ -19,7 +30,13 @@ PRIVATE cancelBtn AS System.Windows.Forms.Button PRIVATE infoStripError AS System.Windows.Forms.ToolStripStatusLabel PRIVATE label1 AS System.Windows.Forms.Label - + PRIVATE LabelType AS System.Windows.Forms.Label + PRIVATE TypeComboBox AS System.Windows.Forms.ComboBox + PRIVATE AppendCheckBox AS System.Windows.Forms.CheckBox + PRIVATE PlaceSolutionInSameDirectory AS System.Windows.Forms.CheckBox + PRIVATE label4 AS System.Windows.Forms.Label + PUBLIC SolutionName AS System.Windows.Forms.TextBox + /// /// Clean up any resources being used. /// @@ -38,7 +55,7 @@ /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// - PRIVATE METHOD InitializeComponent() AS VOID STRICT + PRIVATE METHOD InitializeComponent() AS VOID STRICT SELF:label1 := System.Windows.Forms.Label{} SELF:resultText := System.Windows.Forms.TextBox{} SELF:exportButton := System.Windows.Forms.Button{} @@ -56,15 +73,22 @@ SELF:openButton := System.Windows.Forms.Button{} SELF:backgroundExport := System.ComponentModel.BackgroundWorker{} SELF:cancelBtn := System.Windows.Forms.Button{} + SELF:LabelType := System.Windows.Forms.Label{} + SELF:TypeComboBox := System.Windows.Forms.ComboBox{} + SELF:AppendCheckBox := System.Windows.Forms.CheckBox{} + SELF:PlaceSolutionInSameDirectory := System.Windows.Forms.CheckBox{} + SELF:SolutionName := System.Windows.Forms.TextBox{} + SELF:label4 := System.Windows.Forms.Label{} SELF:infoStrip:SuspendLayout() SELF:SuspendLayout() // // label1 // SELF:label1:AutoSize := true - SELF:label1:Location := System.Drawing.Point{556, 12} + SELF:label1:Location := System.Drawing.Point{417, 10} + SELF:label1:Margin := System.Windows.Forms.Padding{2, 0, 2, 0} SELF:label1:Name := "label1" - SELF:label1:Size := System.Drawing.Size{64, 16} + SELF:label1:Size := System.Drawing.Size{51, 13} SELF:label1:TabIndex := 0 SELF:label1:Text := "Analysis :" // @@ -73,22 +97,22 @@ SELF:resultText:Anchor := ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) ; | System.Windows.Forms.AnchorStyles.Left) ; | System.Windows.Forms.AnchorStyles.Right))) - SELF:resultText:Location := System.Drawing.Point{559, 33} - SELF:resultText:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:resultText:Location := System.Drawing.Point{419, 27} + SELF:resultText:Margin := System.Windows.Forms.Padding{2} SELF:resultText:Multiline := true SELF:resultText:Name := "resultText" SELF:resultText:ReadOnly := true SELF:resultText:ScrollBars := System.Windows.Forms.ScrollBars.Both - SELF:resultText:Size := System.Drawing.Size{257, 118} + SELF:resultText:Size := System.Drawing.Size{266, 223} SELF:resultText:TabIndex := 1 SELF:resultText:WordWrap := false // // exportButton // - SELF:exportButton:Location := System.Drawing.Point{188, 126} - SELF:exportButton:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:exportButton:Location := System.Drawing.Point{169, 231} + SELF:exportButton:Margin := System.Windows.Forms.Padding{2} SELF:exportButton:Name := "exportButton" - SELF:exportButton:Size := System.Drawing.Size{75, 25} + SELF:exportButton:Size := System.Drawing.Size{56, 20} SELF:exportButton:TabIndex := 4 SELF:exportButton:Text := "Export" SELF:exportButton:UseVisualStyleBackColor := true @@ -97,26 +121,27 @@ // label2 // SELF:label2:AutoSize := true - SELF:label2:Location := System.Drawing.Point{15, 11} + SELF:label2:Location := System.Drawing.Point{13, 53} + SELF:label2:Margin := System.Windows.Forms.Padding{2, 0, 2, 0} SELF:label2:Name := "label2" - SELF:label2:Size := System.Drawing.Size{80, 16} + SELF:label2:Size := System.Drawing.Size{67, 13} SELF:label2:TabIndex := 3 SELF:label2:Text := "Input Project" // // pjxPathTextBox // - SELF:pjxPathTextBox:Location := System.Drawing.Point{17, 32} - SELF:pjxPathTextBox:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:pjxPathTextBox:Location := System.Drawing.Point{15, 70} + SELF:pjxPathTextBox:Margin := System.Windows.Forms.Padding{2} SELF:pjxPathTextBox:Name := "pjxPathTextBox" - SELF:pjxPathTextBox:Size := System.Drawing.Size{327, 22} + SELF:pjxPathTextBox:Size := System.Drawing.Size{270, 20} SELF:pjxPathTextBox:TabIndex := 0 // // scxButton // - SELF:scxButton:Location := System.Drawing.Point{349, 32} - SELF:scxButton:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:scxButton:Location := System.Drawing.Point{289, 70} + SELF:scxButton:Margin := System.Windows.Forms.Padding{2} SELF:scxButton:Name := "scxButton" - SELF:scxButton:Size := System.Drawing.Size{44, 23} + SELF:scxButton:Size := System.Drawing.Size{33, 19} SELF:scxButton:TabIndex := 1 SELF:scxButton:Text := "..." SELF:scxButton:UseVisualStyleBackColor := true @@ -124,10 +149,10 @@ // // outputButton // - SELF:outputButton:Location := System.Drawing.Point{349, 91} - SELF:outputButton:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:outputButton:Location := System.Drawing.Point{289, 118} + SELF:outputButton:Margin := System.Windows.Forms.Padding{2} SELF:outputButton:Name := "outputButton" - SELF:outputButton:Size := System.Drawing.Size{44, 23} + SELF:outputButton:Size := System.Drawing.Size{33, 19} SELF:outputButton:TabIndex := 3 SELF:outputButton:Text := "..." SELF:outputButton:UseVisualStyleBackColor := true @@ -135,27 +160,28 @@ // // outputPathTextBox // - SELF:outputPathTextBox:Location := System.Drawing.Point{17, 91} - SELF:outputPathTextBox:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:outputPathTextBox:Location := System.Drawing.Point{15, 118} + SELF:outputPathTextBox:Margin := System.Windows.Forms.Padding{2} SELF:outputPathTextBox:Name := "outputPathTextBox" - SELF:outputPathTextBox:Size := System.Drawing.Size{327, 22} + SELF:outputPathTextBox:Size := System.Drawing.Size{270, 20} SELF:outputPathTextBox:TabIndex := 2 // // label3 // SELF:label3:AutoSize := true - SELF:label3:Location := System.Drawing.Point{15, 70} + SELF:label3:Location := System.Drawing.Point{13, 101} + SELF:label3:Margin := System.Windows.Forms.Padding{2, 0, 2, 0} SELF:label3:Name := "label3" - SELF:label3:Size := System.Drawing.Size{87, 16} + SELF:label3:Size := System.Drawing.Size{71, 13} SELF:label3:TabIndex := 6 SELF:label3:Text := "Output Folder" // // analysisButton // - SELF:analysisButton:Location := System.Drawing.Point{479, 33} - SELF:analysisButton:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:analysisButton:Location := System.Drawing.Point{359, 27} + SELF:analysisButton:Margin := System.Windows.Forms.Padding{2} SELF:analysisButton:Name := "analysisButton" - SELF:analysisButton:Size := System.Drawing.Size{75, 25} + SELF:analysisButton:Size := System.Drawing.Size{56, 20} SELF:analysisButton:TabIndex := 5 SELF:analysisButton:Text := "Analyze" SELF:analysisButton:UseVisualStyleBackColor := true @@ -165,36 +191,36 @@ // SELF:infoStrip:ImageScalingSize := System.Drawing.Size{20, 20} SELF:infoStrip:Items:AddRange({ SELF:infoStripBar, SELF:infoStripLabel, SELF:infoStripError }) - SELF:infoStrip:Location := System.Drawing.Point{0, 170} + SELF:infoStrip:Location := System.Drawing.Point{0, 263} SELF:infoStrip:Name := "infoStrip" - SELF:infoStrip:Padding := System.Windows.Forms.Padding{1, 0, 13, 0} - SELF:infoStrip:Size := System.Drawing.Size{831, 26} + SELF:infoStrip:Padding := System.Windows.Forms.Padding{1, 0, 10, 0} + SELF:infoStrip:Size := System.Drawing.Size{695, 22} SELF:infoStrip:TabIndex := 10 SELF:infoStrip:Text := "statusStrip1" // // infoStripBar // SELF:infoStripBar:Name := "infoStripBar" - SELF:infoStripBar:Size := System.Drawing.Size{100, 18} + SELF:infoStripBar:Size := System.Drawing.Size{75, 16} // // infoStripLabel // SELF:infoStripLabel:Name := "infoStripLabel" - SELF:infoStripLabel:Size := System.Drawing.Size{0, 20} + SELF:infoStripLabel:Size := System.Drawing.Size{0, 17} // // infoStripError // SELF:infoStripError:ForeColor := System.Drawing.Color.Red SELF:infoStripError:IsLink := true SELF:infoStripError:Name := "infoStripError" - SELF:infoStripError:Size := System.Drawing.Size{0, 20} + SELF:infoStripError:Size := System.Drawing.Size{0, 17} // // openButton // - SELF:openButton:Location := System.Drawing.Point{269, 126} - SELF:openButton:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:openButton:Location := System.Drawing.Point{229, 230} + SELF:openButton:Margin := System.Windows.Forms.Padding{2} SELF:openButton:Name := "openButton" - SELF:openButton:Size := System.Drawing.Size{75, 25} + SELF:openButton:Size := System.Drawing.Size{56, 20} SELF:openButton:TabIndex := 12 SELF:openButton:Text := "Open" SELF:openButton:UseVisualStyleBackColor := true @@ -210,21 +236,85 @@ // // cancelBtn // - SELF:cancelBtn:Location := System.Drawing.Point{107, 126} - SELF:cancelBtn:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:cancelBtn:Location := System.Drawing.Point{109, 231} + SELF:cancelBtn:Margin := System.Windows.Forms.Padding{2} SELF:cancelBtn:Name := "cancelBtn" - SELF:cancelBtn:Size := System.Drawing.Size{75, 25} + SELF:cancelBtn:Size := System.Drawing.Size{56, 20} SELF:cancelBtn:TabIndex := 13 SELF:cancelBtn:Text := "Cancel" SELF:cancelBtn:UseVisualStyleBackColor := true SELF:cancelBtn:Visible := false SELF:cancelBtn:Click += System.EventHandler{ SELF, @cancelBtn_Click() } // + // LabelType + // + SELF:LabelType:AutoSize := true + SELF:LabelType:Location := System.Drawing.Point{12, 10} + SELF:LabelType:Margin := System.Windows.Forms.Padding{2, 0, 2, 0} + SELF:LabelType:Name := "LabelType" + SELF:LabelType:Size := System.Drawing.Size{70, 13} + SELF:LabelType:TabIndex := 14 + SELF:LabelType:Text := "Project Type:" + // + // TypeComboBox + // + SELF:TypeComboBox:DropDownStyle := System.Windows.Forms.ComboBoxStyle.DropDownList + SELF:TypeComboBox:FormattingEnabled := true + SELF:TypeComboBox:Items:AddRange({ "Windows EXE", "Class Library (DLL)", "Console App" }) + SELF:TypeComboBox:Location := System.Drawing.Point{14, 26} + SELF:TypeComboBox:Name := "TypeComboBox" + SELF:TypeComboBox:Size := System.Drawing.Size{123, 21} + SELF:TypeComboBox:TabIndex := 15 + // + // AppendCheckBox + // + SELF:AppendCheckBox:AutoSize := true + SELF:AppendCheckBox:Location := System.Drawing.Point{143, 28} + SELF:AppendCheckBox:Name := "AppendCheckBox" + SELF:AppendCheckBox:Size := System.Drawing.Size{179, 17} + SELF:AppendCheckBox:TabIndex := 16 + SELF:AppendCheckBox:Text := "Append to existing Solution (.sln)" + SELF:AppendCheckBox:UseVisualStyleBackColor := true + // + // PlaceSolutionInSameDirectory + // + SELF:PlaceSolutionInSameDirectory:AutoSize := true + SELF:PlaceSolutionInSameDirectory:Location := System.Drawing.Point{16, 198} + SELF:PlaceSolutionInSameDirectory:Name := "PlaceSolutionInSameDirectory" + SELF:PlaceSolutionInSameDirectory:Size := System.Drawing.Size{248, 17} + SELF:PlaceSolutionInSameDirectory:TabIndex := 17 + SELF:PlaceSolutionInSameDirectory:Text := "Place solution and project in the same directory" + SELF:PlaceSolutionInSameDirectory:UseVisualStyleBackColor := true + // + // SolutionName + // + SELF:SolutionName:Location := System.Drawing.Point{16, 169} + SELF:SolutionName:Margin := System.Windows.Forms.Padding{2} + SELF:SolutionName:Name := "SolutionName" + SELF:SolutionName:Size := System.Drawing.Size{270, 20} + SELF:SolutionName:TabIndex := 18 + // + // label4 + // + SELF:label4:AutoSize := true + SELF:label4:Location := System.Drawing.Point{14, 152} + SELF:label4:Margin := System.Windows.Forms.Padding{2, 0, 2, 0} + SELF:label4:Name := "label4" + SELF:label4:Size := System.Drawing.Size{76, 13} + SELF:label4:TabIndex := 19 + SELF:label4:Text := "Solution Name" + // // ExportProjectWindow // - SELF:AutoScaleDimensions := System.Drawing.SizeF{8, 16} + SELF:AutoScaleDimensions := System.Drawing.SizeF{6, 13} SELF:AutoScaleMode := System.Windows.Forms.AutoScaleMode.Font - SELF:ClientSize := System.Drawing.Size{831, 196} + SELF:ClientSize := System.Drawing.Size{695, 285} + SELF:Controls:Add(SELF:SolutionName) + SELF:Controls:Add(SELF:label4) + SELF:Controls:Add(SELF:PlaceSolutionInSameDirectory) + SELF:Controls:Add(SELF:AppendCheckBox) + SELF:Controls:Add(SELF:TypeComboBox) + SELF:Controls:Add(SELF:LabelType) SELF:Controls:Add(SELF:cancelBtn) SELF:Controls:Add(SELF:openButton) SELF:Controls:Add(SELF:infoStrip) @@ -238,16 +328,16 @@ SELF:Controls:Add(SELF:exportButton) SELF:Controls:Add(SELF:resultText) SELF:Controls:Add(SELF:label1) - SELF:Margin := System.Windows.Forms.Padding{3, 2, 3, 2} + SELF:Margin := System.Windows.Forms.Padding{2} SELF:Name := "ExportProjectWindow" SELF:Text := "Export Project" SELF:infoStrip:ResumeLayout(false) SELF:infoStrip:PerformLayout() SELF:ResumeLayout(false) SELF:PerformLayout() - END METHOD - + END METHOD + #endregion - + END CLASS END NAMESPACE diff --git a/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.prg b/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.prg index 103faf5593..5ba5668fab 100644 --- a/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.prg +++ b/src/Tools/VFPXPorter/Source/VFPXPorter/ExportProjectWindow.prg @@ -16,10 +16,15 @@ BEGIN NAMESPACE VFPXPorter PROPERTY Settings AS XPorterSettings AUTO PUBLIC CONSTRUCTOR() STRICT //ExportWindow - SELF:InitializeComponent() + SELF:InitializeComponent() + SELF:TypeComboBox:SelectedIndex := 0 RETURN PRIVATE METHOD exportButton_Click(sender AS OBJECT, e AS System.EventArgs) AS VOID + SELF:Settings:OutputType := (ProjectType)SELF:TypeComboBox:SelectedIndex + SELF:Settings:AppendToSolution := SELF:AppendCheckBox:Checked + SELF:Settings:SolutionName := SELF:SolutionName:Text + SELF:Settings:PlaceSolutionInSameDirectory := SELF:PlaceSolutionInSameDirectory:Checked // SELF:infoStripLabel:Text := "" IF !SELF:CheckFileAndFolder() @@ -29,8 +34,9 @@ BEGIN NAMESPACE VFPXPorter IF !SELF:xPorter:ProcessPJX() MessageBox.Show( "Error during analyzing operation.", "Analyzing Project", MessageBoxButtons.OK, MessageBoxIcon.Error ) RETURN - ENDIF - // + ENDIF + + // // DoBackup, ProcessFirst SELF:Processing( TRUE ) SELF:backgroundExport:RunWorkerAsync() @@ -55,7 +61,12 @@ BEGIN NAMESPACE VFPXPorter ofd:DefaultExt := "PJX" ofd:Filter := "Pjx files (*.pjx)|*.pjx|All files (*.*)|*.*" IF ( ofd:ShowDialog() == DialogResult.OK ) - SELF:pjxPathTextBox:Text := ofd:FileName + SELF:pjxPathTextBox:Text := ofd:FileName + + IF String.IsNullOrWhiteSpace(SELF:SolutionName:Text) + // Auto-Complete: if solution name is empty, we set it to the PJX folder + SELF:SolutionName:Text := Path.GetFileNameWithoutExtension(ofd:FileName) + ENDIF ENDIF RETURN PRIVATE METHOD outputButton_Click(sender AS OBJECT, e AS System.EventArgs) AS VOID STRICT @@ -68,44 +79,74 @@ BEGIN NAMESPACE VFPXPorter IF ( fbd:ShowDialog() == DialogResult.OK ) SELF:outputPathTextBox:Text := fbd:SelectedPath ENDIF - RETURN + RETURN + PRIVATE METHOD CheckFileAndFolder() AS LOGIC // SELF:infoStripLabel:Text := "" - SELF:infoStripError:Text := "" - SELF:infoStripLabel:ForeColor := Color.Black + SELF:infoStripError:Text := "" + SELF:infoStripLabel:ForeColor := Color.Black + VAR pjxFilePath := SELF:pjxPathTextBox:Text - VAR outputPath := SELF:outputPathTextBox:Text + VAR baseOutputPath := SELF:outputPathTextBox:Text + // - IF !File.Exists( pjxFilePath) + IF !File.Exists(pjxFilePath) SELF:infoStripLabel:ForeColor := Color.Red SELF:infoStripLabel:Text := "Error : Input Project doesn't exist." RETURN FALSE - ENDIF - IF !Directory.Exists( outputPath ) + ENDIF + + IF !Directory.Exists(baseOutputPath) SELF:infoStripLabel:ForeColor := Color.Red SELF:infoStripLabel:Text := "Error : Output Path doesn't exist." RETURN FALSE - ENDIF + ENDIF + // + // Get the PJX File name, and use it as a SubFolder - LOCAL destFile AS STRING - destFile := Path.GetFileNameWithoutExtension( pjxFilePath ) - outputPath := Path.Combine( outputPath, destFile ) + VAR projectName := Path.GetFileNameWithoutExtension(pjxFilePath) + LOCAL projectPath AS STRING + + IF !SELF:Settings:PlaceSolutionInSameDirectory + projectPath := Path.Combine(baseOutputPath, projectName) + ELSE + projectPath := baseOutputPath + ENDIF + // Warning, we may NOT be able to create the Directory TRY - IF Directory.Exists( outputPath ) - SELF:EraseFolder( outputPath, FALSE ) + IF Directory.Exists(projectPath) + // If Append: do not delete the folder. We might delete the solution. + // or sibling project folders. We just delete if not Append. + IF !SELF:Settings:AppendToSolution + SELF:EraseFolder(projectPath, FALSE ) + ELSE + // If Append: we just delete sub-folders of the current project + // to ensure a clean export of the current project. + VAR codeDir := Path.Combine(projectPath, "Code") + IF Directory.Exists(codeDir) + SELF:EraseFolder(codeDir, TRUE) + ENDIF + var xsharpDir := Path.Combine(projectPath, "XSharp") + IF Directory.Exists(xsharpDir) + SELF:EraseFolder(xsharpDir, TRUE) + ENDIF + ENDIF + ELSE + Directory.CreateDirectory(projectPath) ENDIF - Directory.CreateDirectory( outputPath ) CATCH e AS Exception // - SELF:resultText:Text := "Cannot delete Folder : " + e.Message - IF !SELF:Settings:IgnoreErrors - THROW e + SELF:resultText:Text := "Warning during folder cleanup: " + e:Message + IF !SELF:Settings:IgnoreErrors + THROW e ENDIF END TRY // - SELF:xPorter := XPorterProject{ pjxFilePath, outputPath } + SELF:xPorter := XPorterProject{ pjxFilePath, projectPath } + SELF:xPorter:SolutionPath := baseOutputPath + RETURN TRUE PRIVATE METHOD analysisButton_Click(sender AS OBJECT, e AS System.EventArgs) AS VOID STRICT IF !SELF:CheckFileAndFolder() diff --git a/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSProject.prg b/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSProject.prg index 590d3413f0..4e58aa319d 100644 --- a/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSProject.prg +++ b/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSProject.prg @@ -28,10 +28,41 @@ CLASS VSProject PROPERTY Name AS STRING AUTO - PROPERTY IsLibrary AS LOGIC AUTO + PROPERTY IsLibrary AS LOGIC + GET + RETURN SELF:ProjectType == ProjectType.ClassLibrary + END GET + SET + IF value + SELF:ProjectType := ProjectType.ClassLibrary + ELSE + SELF:ProjectType := ProjectType.WindowsExe + ENDIF + END SET + END PROPERTY + + /// + /// Gets or sets the type of Visual Studio project to generate. + /// + /// + /// This value controls how the project is emitted in the generated + /// solution and project files. Typical values include + /// and + /// . + /// + PROPERTY ProjectType AS ProjectType AUTO PROPERTY GUID AS STRING AUTO + /// + /// Gets or sets the path to the project file relative to the solution directory. + /// + /// + /// This value is used when generating the solution so that project entries + /// reference the project file using a stable relative path. + /// + PROPERTY RelativePath AS STRING AUTO + PROPERTY FrameworkVersion AS STRING AUTO PROPERTY XmlDoc as XmlDocument AUTO @@ -45,6 +76,7 @@ CLASS VSProject SELF:GUID := System.Guid.NewGuid().ToString("B"):ToUpper() SELF:ProjectReferenceList := List{} SELF:IsLibrary := FALSE + SELF:ProjectType := ProjectType.WindowsExe // TODO : set the Framework version as a Setting ?? SELF:FrameworkVersion := "4.7.2" RETURN @@ -212,10 +244,35 @@ CLASS VSProject //parent:AppendChild( dummy ) SELF:CreateElement(parent, "Name", name + "App") SELF:CreateElement(parent, "ProjectGuid", SELF:GUID) - SELF:CreateElement(parent, "OutputType", IIF(SELF:IsLibrary, "Library", "WinExe")) + // Mapping MSBuild project types + LOCAL outputTypeStr AS STRING + SWITCH SELF:ProjectType + CASE ProjectType.ClassLibrary + outputTypeStr := "Library" + + CASE ProjectType.Console + outputTypeStr := "Exe" + + OTHERWISE + outputTypeStr := "WinExe" + END SWITCH + SELF:CreateElement(parent, "OutputType", outputTypeStr) + SELF:CreateElement(parent, "AppDesignerFolder","Properties") SELF:CreateElement(parent, "RootNamespace", name) - SELF:CreateElement(parent, "AssemblyName", IIF(SELF:IsLibrary, name + "Lib", name + "App")) + + // Assembly name according to type + LOCAL assemblyName AS STRING + SWITCH SELF:ProjectType + CASE ProjectType.ClassLibrary + assemblyName := Name + "Lib" + CASE ProjectType.Console + assemblyName := Name + "Console" + OTHERWISE + assemblyName := Name + "App" + END SWITCH + SELF:CreateElement(parent, "AssemblyName", assemblyName) + SELF:CreateElement(parent, "TargetFrameworkVersion", SELF:FrameworkVersion) SELF:CreateElement(parent, "NoLogo", "true") SELF:CreateElement(parent, "GenerateFullPaths", "true") diff --git a/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSSolution.prg b/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSSolution.prg index 5ff8ee16e3..bf546d5df3 100644 --- a/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSSolution.prg +++ b/src/Tools/VFPXPorter/Source/VFPXPorterLib/VSSolution.prg @@ -1,13 +1,14 @@ // VSSolution.prg // Created by : fabri // Creation Date : 11/20/2019 1:00:07 PM -// Created for : +// Created for : // WorkStation : FABPORTABLE USING System USING System.Collections.Generic USING System.Text +USING System.Linq BEGIN NAMESPACE VFPXPorterLib @@ -15,66 +16,111 @@ BEGIN NAMESPACE VFPXPorterLib /// The VSSolution class. /// CLASS VSSolution - + PROPERTY Projects AS List AUTO - + PROPERTY GUID AS STRING AUTO - + PUBLIC CONSTRUCTOR( ) SELF:Projects := List{} SELF:GUID := System.Guid.NewGuid().ToString("B"):ToUpper() - + PUBLIC METHOD Save( solutionPath AS STRING ) AS VOID VAR sb := StringBuilder{} - // + sb:AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00") sb:AppendLine("# Visual Studio Version 15") sb:AppendLine("VisualStudioVersion = 15.0.28307.1525") - sb:AppendLine("MinimumVisualStudioVersion = 10.0.40219.1") + sb:AppendLine("MinimumVisualStudioVersion = 10.0.40219.1") + FOREACH xsProj AS VSProject IN SELF:Projects sb:Append(e"Project(\"{AA6C8D78-22FF-423A-9C7C-5F2393824E04}\") = ") - sb:Append('"') - sb:Append(xsProj:Name ) - sb:Append('"') - sb:Append(", ") - sb:Append('"') - sb:Append(xsProj:Name+".xsproj" ) - sb:Append('"') - sb:Append(", ") - sb:Append('"') - sb:Append(xsProj:GUID) - sb:Append('"') - sb:AppendLine() + sb:Append(i"""" + xsProj:Name + """, ") + + VAR cPath := IIF(String.IsNullOrEmpty(xsProj:RelativePath), xsProj:Name + ".xsproj", xsProj:RelativePath) + sb:Append("""" + cPath + """, ") + + sb:Append("""" + xsProj:GUID + """") + + sb:AppendLine() sb:AppendLine("EndProject") NEXT // - sb:AppendLine("Global") - sb:AppendLine("GlobalSection(SolutionConfigurationPlatforms) = preSolution") - sb:AppendLine("Debug|Any CPU = Debug|Any CPU") - sb:AppendLine("Release|Any CPU = Release|Any CPU") - sb:AppendLine("EndGlobalSection") - sb:AppendLine("GlobalSection(ProjectConfigurationPlatforms) = postSolution") - FOREACH xsProj AS VSProject IN SELF:Projects - sb:Append(xsProj:GUID) - sb:AppendLine(".Debug|Any CPU.ActiveCfg = Debug|Any CPU") - sb:Append(xsProj:GUID) - sb:AppendLine(".Debug|Any CPU.Build.0 = Debug|Any CPU") - sb:Append(xsProj:GUID) - sb:AppendLine(".Release|Any CPU.ActiveCfg = Release|Any CPU") - sb:Append(xsProj:GUID) - sb:AppendLine(".Release|Any CPU.Build.0 = Release|Any CPU") - NEXT - sb:AppendLine("EndGlobalSection") - sb:AppendLine("GlobalSection(SolutionProperties) = preSolution") - sb:AppendLine("HideSolutionNode = FALSE") - sb:AppendLine("EndGlobalSection") - sb:AppendLine("GlobalSection(ExtensibilityGlobals) = postSolution") - sb:Append("SolutionGuid = ") - sb:AppendLine( SELF:GUID ) - sb:AppendLine("EndGlobalSection") + sb:AppendLine("Global") + + // Indentation with \t to VS doesn't mark the file as dirty + sb:AppendLine(e"\tGlobalSection(SolutionConfigurationPlatforms) = preSolution") + sb:AppendLine(e"\t\tDebug|Any CPU = Debug|Any CPU") + sb:AppendLine(e"\t\tRelease|Any CPU = Release|Any CPU") + sb:AppendLine(e"\tEndGlobalSection") + + + sb:AppendLine(e"\tGlobalSection(ProjectConfigurationPlatforms) = postSolution") + FOREACH xsProj AS VSProject IN SELF:Projects + sb:Append(e"\t\t" + xsProj:GUID) + sb:AppendLine(".Debug|Any CPU.ActiveCfg = Debug|Any CPU") + sb:Append(e"\t\t" + xsProj:GUID) + sb:AppendLine(".Debug|Any CPU.Build.0 = Debug|Any CPU") + sb:Append(e"\t\t" + xsProj:GUID) + sb:AppendLine(".Release|Any CPU.ActiveCfg = Release|Any CPU") + sb:Append(e"\t\t" + xsProj:GUID) + sb:AppendLine(".Release|Any CPU.Build.0 = Release|Any CPU") + NEXT + sb:AppendLine(e"\tEndGlobalSection") + + sb:AppendLine(e"\tGlobalSection(SolutionProperties) = preSolution") + sb:AppendLine(e"\t\tHideSolutionNode = FALSE") + sb:AppendLine(e"\tEndGlobalSection") + + sb:AppendLine(e"\tGlobalSection(ExtensibilityGlobals) = postSolution") + sb:Append(e"\t\tSolutionGuid = ") + sb:AppendLine( SELF:GUID ) + sb:AppendLine(e"\tEndGlobalSection") + sb:AppendLine("EndGlobal") // - System.IO.File.WriteAllText( solutionPath, sb:ToString() ) - + System.IO.File.WriteAllText( solutionPath, sb:ToString() ) + RETURN + + /// + /// Try loading projects from an existing solution. + /// + PUBLIC METHOD Load( solutionPath AS STRING ) AS LOGIC + IF !System.IO.File.Exists( solutionPath ) + RETURN FALSE + ENDIF + + LOCAL lines := System.IO.File.ReadAllLines( solutionPath ) AS STRING[] + + FOREACH VAR line IN lines + // We look for lines that define projects: Project("{GUID}") = "Name", "Path.xsproj", "{GUID}" + IF line:Trim():StartsWith("Project(") + VAR parts := line:Split( { ',', '=', '"', '(', ')', ' ' }, StringSplitOptions.RemoveEmptyEntries) + + // Expected structure: + // [1] Project + // [2] {TypeGUID} + // [3] Name + // [4] Path + // [5] {ProjGUID} + IF parts:Length >= 5 + VAR projName := parts[3]:Trim() + VAR projPath := parts[4]:Trim() + VAR projGuid := parts[5]:Trim() + + // We avoid duplicates if the project is already on our list + IF !SELF:Projects:Any({ p => String.Compare(p:Name, projName, TRUE) == 0 }) + // we add a project proxy (just name and GUID) + VAR p := VSProject{ projName } + p:GUID := projGuid // we keep the original GUID to VS does not get confused + p:RelativePath := projPath // to avoid losing the subfolder + + SELF:Projects:Add( p ) + ENDIF + ENDIF + ENDIF + NEXT + RETURN TRUE + END CLASS -END NAMESPACE // global::FabVFPXPorter.VSSupport \ No newline at end of file +END NAMESPACE // global::FabVFPXPorter.VSSupport diff --git a/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterProject.prg b/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterProject.prg index 3d2027e55d..9c87dcf5ce 100644 --- a/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterProject.prg +++ b/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterProject.prg @@ -57,6 +57,8 @@ BEGIN NAMESPACE VFPXPorterLib PROPERTY CurrentFileName AS STRING AUTO GET PRIVATE SET + PROPERTY SolutionPath AS STRING AUTO + CONSTRUCTOR( filePath AS STRING, destPath AS STRING ) // @@ -513,7 +515,7 @@ BEGIN NAMESPACE VFPXPorterLib File.WriteAllText( vfpxporterPath, headerText ) ENDIF // And Don't forget the Starting block - IF SELF:Project:Main != NULL + IF SELF:Project:Main != NULL .AND. SELF:Settings:OutputType == ProjectType.WindowsExe // The File to be created LOCAL destFile AS STRING destFile := Path.GetFileName( SELF:StartBlockFile ) @@ -583,8 +585,8 @@ BEGIN NAMESPACE VFPXPorterLib // Generate a MSBuild file PRIVATE METHOD GenerateSolution( stdDef AS STRING ) AS VOID - LOCAL destFile AS STRING LOCAL xsLibs := NULL AS VSProject + // First, do we have any "Libraries" ? IF SELF:GeneratedLibFiles:Count > 0 xsLibs := VSProject{ "ClassLibraries" } @@ -608,17 +610,19 @@ BEGIN NAMESPACE VFPXPorterLib ENDIF NEXT // - destFile := Path.Combine( SELF:outputPath, "ClassLibraries" ) - destFile := Path.ChangeExtension( destFile, "xsproj") + VAR libProjPath := Path.Combine( SELF:outputPath, "ClassLibraries.xsproj") // Save the MSBuild file for the Libraries - xsLibs:Save( destFile, stdDef ) + xsLibs:Save( libProjPath, stdDef ) ENDIF + // Now the Main Project - destFile := Path.GetFileName( SELF:pjxFilePath ) - destFile := Path.Combine( SELF:outputPath, destFile ) - destFile := Path.ChangeExtension( destFile, "xsproj") + VAR projectName := Path.GetFileNameWithoutExtension( SELF:pjxFilePath ) + VAR projectPath := Path.Combine( SELF:outputPath, projectName + ".xsproj") + // The imported Project : We will add "App" at the end of the ProjectName to avoid conflicts in the Name Property - VAR xsProj := VSProject{ Path.GetFileNameWithoutExtension( SELF:pjxFilePath )} + VAR xsProj := VSProject{ projectName } + xsProj:ProjectType := SELF:Settings:OutputType + SELF:AddStandardReferences( xsProj ) // FOREACH refFile AS Reference IN SELF:ReferenceFiles @@ -642,18 +646,53 @@ BEGIN NAMESPACE VFPXPorterLib xsProj:ProjectReferenceList:Add( xsLibs ) ENDIF // Save the MSBuild file for the "main" Project - xsProj:Save( destFile, stdDef ) + xsProj:Save( projectPath, stdDef ) + + LOCAL relativeProjPath AS STRING + IF SELF:Settings:PlaceSolutionInSameDirectory + relativeProjPath := projectName + ".xsproj" + ELSE + relativeProjPath := Path.Combine(projectName, projectName + ".xsproj") + ENDIF + // Now the Solution VAR xsSolution := VSSolution{} + + LOCAL solutionBasePath AS STRING + IF String.IsNullOrWhiteSpace(SELF:SolutionPath) + solutionBasePath := String.Empty + ELSE + solutionBasePath := SELF:SolutionPath:TrimEnd(Path.DirectorySeparatorChar) + ENDIF + + LOCAL solutionName AS STRING + IF !String.IsNullOrWhiteSpace(SELF:Settings:SolutionName) + solutionName := SELF:Settings:SolutionName + ELSE + solutionName := Path.GetFileName(solutionBasePath) + ENDIF + + VAR solutionFile := Path.Combine(solutionBasePath, solutionName + ".sln") + + // If user checked to Append to existing Solution, load it + IF SELF:Settings:AppendToSolution .AND. File.Exists( solutionFile ) + xsSolution:Load( solutionFile ) + ENDIF + + VAR existing := xsSolution:Projects:Find({ p => String.Compare(p:Name, xsProj:Name, TRUE) == 0 }) + IF existing != NULL + xsSolution:Projects:Remove(existing) + ENDIF + + xsProj:RelativePath := relativeProjPath xsSolution:Projects:Add( xsProj ) - IF xsLibs != NULL + + IF xsLibs != NULL .AND. !xsSolution:Projects:Any({ p => String.Compare(p:Name, xsLibs:Name, TRUE) == 0 }) xsSolution:Projects:Add( xsLibs ) ENDIF - destFile := Path.GetFileName( SELF:pjxFilePath ) - destFile := Path.Combine( SELF:outputPath, destFile ) - destFile := Path.ChangeExtension( destFile, "sln") - xsSolution:Save( destFile ) - // + + xsSolution:Save( solutionFile ) + RETURN // Backup the PJX Items : Create an XML File with Items info, and export the associated Code diff --git a/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterSettings.prg b/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterSettings.prg index c1231f8c49..d119f5f9d5 100644 --- a/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterSettings.prg +++ b/src/Tools/VFPXPorter/Source/VFPXPorterLib/XPorterSettings.prg @@ -11,6 +11,27 @@ USING System.Text BEGIN NAMESPACE VFPXPorterLib +/// +/// Specifies the type of project to generate for the exported code. +/// Use for a Windows GUI executable, +/// for a reusable class library (DLL), +/// or for a console application. +/// +ENUM ProjectType + /// + /// Windows GUI executable project (Win32/desktop application without a console window). + /// + MEMBER WindowsExe := 0 + /// + /// Class library project that produces a reusable DLL. + /// + MEMBER ClassLibrary := 1 + /// + /// Console application project that runs in a command-line window. + /// + MEMBER Console := 2 +END ENUM + /// /// The XPorterSettings class. /// @@ -73,7 +94,27 @@ CLASS XPorterSettings PUBLIC STATIC PROPERTY SingleNoContainerFormEndTypeFile AS STRING GET XPorterSettings.SingleFolder + "\\endtypeNotContainer.prg" PUBLIC STATIC PROPERTY SingleNoContainerFormInitTypeFile AS STRING GET XPorterSettings.SingleFolder + "\\inittypeNotContainer.prg" -#endregion + #endregion + + /// + /// Gets or sets the type of .NET project to be generated (for example Windows executable, class library, or console application). + /// + PROPERTY OutputType AS ProjectType AUTO + + /// + /// Gets or sets a value indicating whether the generated project should be appended to an existing solution instead of creating a new one. + /// + PROPERTY AppendToSolution AS LOGIC AUTO + + /// + /// Gets or sets a value indicating whether the solution file should be placed in the same directory as the generated project. + /// + PROPERTY PlaceSolutionInSameDirectory AS LOGIC AUTO + + /// + /// Gets or sets the name of the generated solution when creating or updating a solution. + /// + PROPERTY SolutionName AS STRING AUTO CONSTRUCTOR() @@ -96,7 +137,11 @@ CLASS XPorterSettings SELF:StoreInFolders := FALSE SELF:EmptyFolder := TRUE SELF:PrefixEvent := FALSE - SELF:KeepFoxProEventName := TRUE + SELF:KeepFoxProEventName := TRUE + SELF:OutputType := ProjectType.WindowsExe // Exe by default + SELF:AppendToSolution := FALSE // New Solution by default + SELF:PlaceSolutionInSameDirectory := FALSE // New Solution folder by default + SELF:SolutionName := "" RETURN ///