PowerShell: AL-Objektsplitter

14. August 2019 17:02

AL-Dateien werden ja normalerweise einzeln erzeugt, aber wenn man zu Abgleichszwecken daraus ein Objektpaket zusammenstellt, sind anschließend die alten C/AL-Objektsplitter natürlich nicht mehr verwendbar, wenn das wieder zerlegt werden soll.

Mit diesem Skript ist das machbar (zumindest für die AL-Dateien aus unserem Add-on, die alle noch eine Objekt-ID enthalten, funktioniert es einwandfrei :-) , falls mit anderen Dateien Fehler auftauchen, bitte Beispiele reinstellen).

Die Dateien werden ausgehehend von dem Ordner, wo das Objektpaket liegt (das kann über ein Fenster ausgewählt werden)…
OpenALFile.png
…in einem Unterordner SPLITBCOBJ erzeugt.

Bei der Dateinamenstruktur für AL-Dateien kursieren ja mittlerweile verschiedene Varianten.
Im Skript werden weitgehend die Namensformatierungen des Txt2Al-Tools verwendet (nicht, weil ich die schön finde, sondern weil sie eben so sind, wie sie sind :mrgreen: ), nur bei Extensions (für Tables und Pages) fehlt die Nummer des Objekts, auf das die Extension angewandt wird, weil sich dieses aus dem Dateiinhalt nicht entnehmen lässt. Ansonsten sind andere Namensstrukturen natürlich im Skript nach Bedarf anpassbar.

ALObjectSplitter.png


Code:
function Split-ALObjectFile
{
  $ErrorActionPreference = "Stop"
  $PSDefaultParameterValues['*:ErrorAction']='Stop'
  Function Get-FileName($initialDirectory)
  {
    [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
   
    $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.initialDirectory = $initialDirectory
    $OpenFileDialog.filter = "AL Object Files (*.al)|*.al"
    $OpenFileDialog.ShowDialog() | Out-Null
    $OpenFileDialog.filename
  }
       
  $inputfile = Get-FileName "C:\temp" # This is the default path in OpenFile window.
  if ($inputfile -eq "") {throw 'Please select a file'}
     
  [decimal]$filesize = ((Get-Item $inputfile).length/1MB)
  $filesize =[math]::round($filesize,2)
  $inputfile = resolve-path $inputfile
  $WorkingFolder = Split-Path -Parent $inputfile
  if (Test-path "$WorkingFolder\SPLITBCOBJ\")
    {Remove-Item -path "$WorkingFolder\SPLITBCOBJ\" -Recurse -Force}


  Write-Host "Splitting BC/NAV objects from $inputfile (Size: $filesize MB) to $WorkingFolder\SPLITBCOBJ\, this may take a while..." -ForegroundColor Yellow
  $starttime = date
   
  $Sr = new-object System.IO.StreamReader($inputfile,[system.text.encoding]::GetEncoding(65001))
  [int]$ObjCnt = 0
  [int]$TabCnt = 0
  [int]$PagCnt = 0
  [int]$RepCnt = 0
  [int]$CodCnt = 0
  [int]$XmlCnt = 0
  [int]$QueCnt = 0
  [int]$TabExtCnt = 0
  [int]$PagExtCnt = 0
  [int]$EnumCnt = 0
  [int]$EnumExtCnt = 0
  [int]$CtrlAddInCnt = 0
  [int]$PagCstmCnt = 0
  [int]$KeysCnt = 0
  [int]$DotNetCnt = 0
  [int]$ProfileCnt = 0

  while (-not $Sr.EndOfStream)
  {
    $Currline = $sr.ReadLine()
       
    if ($Currline.StartsWith('table ')) {$NewObject = $True}
    Elseif ($Currline.StartsWith('tableextension ')) {$NewObject = $True}
    Elseif  ($Currline.StartsWith('page ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('pageextension ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('pagecustomization ')) {$NewObject = $True}
    Elseif  ($Currline.StartsWith('report ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('query ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('codeunit ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('enum ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('enumextension ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('keys ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('profile ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('xmlport ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('dotnet ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('controladdin ')) {$NewObject = $True}
    else {$NewObject = $false}
         

       
    if ($NewObject)
    {
      $IsExtension = $false
      $dblquotes = [char]34
      [String]$ObjectChars = $Currline.Substring(0,5)
      [String]$ObjectChar = $Currline.Substring(6,1)
         
      write-host $currline
      $IsExtension = ($Currline.indexof(' extends ') -gt 0)
      if ($IsExtension)
      {
        $PosExtends = $Currline.indexof(' extends ')
        if ($PosExtends -gt 0)
        {
          $ObjectExtendedName = $Currline.Substring($PosExtends+9)
          $CleanObjectExtendedName = $ObjectExtendedName.TrimStart('"')
          $CleanObjectExtendedName = $CleanObjectExtendedName.TrimEnd(' ')
          $CleanObjectExtendedName = $CleanObjectExtendedName.TrimEnd('"')
          $CleanObjectExtendedName = $CleanObjectExtendedName.Trim('/')     
        }
        else
        {$CleanObjectExtendedName = ''}
      }
      else
      {$CleanObjectExtendedName = ''}
         
      #write-host $ObjectChars
      #write-host $ObjectChar
      $PosFirstBlank =  $Currline.indexof(' ')
      $Currline2 = $Currline.Substring($PosFirstBlank+1)
      $PosSecondBlank =  $Currline2.indexof(' ')
      if ($PosSecondBlank -ne 0)
      {$ObjectName = $Currline2.Substring($PosSecondBlank+1)}
      else
      {$ObjectName = ''}
         
      if ($ObjectName.Startswith('"'))
      {
        $CleanObjectName = $ObjectName.TrimStart('"')
        $CleanObjectName = $CleanObjectName.TrimEnd(' ')
        $CleanObjectName = $CleanObjectName.TrimEnd('"')
        $CleanObjectName = $CleanObjectName.Trim('/')
      }
      else
      {$CleanObjectName = $ObjectName.Trim('/')}
         
      write-host $currline2
      $ObjectLine = $currline.Split(' ')
      $ObjCnt++
      Switch ($ObjectChars)
      {
        'table' {
          if (!$IsExtension)                 
          {
            $TabCnt++;$ObjectType = 'Table'
          }
          else
          {
            $TabExtCnt++;$ObjectType = 'TableExt'
          }
                         
        }
             
        'page ' {
        $PagCnt++;$ObjectType = 'Page'}
 
        'pagee' {$PagExtCnt++; $ObjectType = 'PageExt'}
        'pagec' {$PagCstmCnt++;$ObjectType = 'PageCstm'}
        'repor' {$RepCnt++;$ObjectType = 'Report'}
        'codeu'{$CodCnt++;$ObjectType = 'Codeunit'}
        'xmlpo' {$XmlCnt++;$ObjectType = 'XMLport'}
        'enum ' {$enumCnt++;$ObjectType = 'Enum'}
        'enume' {$enumExtCnt++;$ObjectType = 'EnumExt'}
        'keys ' {$KeysCnt++;$ObjectType = 'Keys'}
        'profi' {$ProfileCnt++;$ObjectType = 'Profile'}
        'query ' {$QueCnt++;$ObjectType = 'Query'}
        'dotnet ' {$DotNetCnt++;$ObjectType = 'DotNet'}
               
        'controladdin ' {$CtrlAddinCnt++;$ObjectType = 'ControlAddin'}
       
      }

           
         
      if (!$IsExtension)
      {
        If (($ObjectLine[2] -eq '') -and ($CleanObjectName -eq ''))
               
        {$ObjectFileName = $ObjectType + '.al'}
           
        elseif (($ObjectLine[1] -ne '') -and ($CleanObjectName -eq ''))
           
        {$ObjectFileName = $ObjectType + $ObjectLine[1] + '.al'}
           
        elseif (($ObjectLine[1] -ne '') -and ($CleanObjectName -eq ''))
           
        {$ObjectFileName = $ObjectType + $ObjectLine[1] + $CleanObjectName + '.al'}
           
        else
        {$ObjectFileName = $ObjectType + ' ' + $ObjectLine[1] + ' - ' + $CleanObjectName + '.al'}
      }
      else
      {
             
             
        $ObjectFileName = 'Modification  - ' + $CleanObjectExtendedName + '(' + $ObjectType + ')' + '.al'
      }
           
           
      #write-host "Object Type: $Objecttype"   
      #write-host "Object Name: $CleanObjectName"
      #write-host 'Object ID: ' $ObjectLine[1]
      #write-host "Object Extended Name: $CleanObjectExtendedName"
      $ObjectFileName = $ObjectFileName.replace('/','')
      write-host "File Name: $ObjectFileName"
      $Objectfile = New-Item -path "$WorkingFolder\SPLITBCOBJ\$ObjectFileName" -type file -force
           
      $sw = new-object System.IO.Streamwriter($Objectfile,$false,[system.text.encoding]::GetEncoding(65001))
      #IF (Test-Path $ObjectFile) {Remove-Item $ObjectFile}
       
    }
       
    if (-not $Currline.StartsWith('}'))
    {$sw.writeline($Currline)}
    else
    {
      $sw.writeline($Currline)
      $sw.writeline()
      $sw.Flush()

    }

       
  }
   
  $endtime = date
  $time = $endtime - $starttime
  Write-Host "$ObjCnt BC/NAV objects splitted to $WorkingFolder\SPLITBCOBJ\ in $($time.Minutes)m:$($time.Seconds)s:$($time.Milliseconds)ms" -ForegroundColor Yellow
  Write-Host "Tables: $TabCnt" -ForegroundColor Yellow
  Write-Host "Table Extensions: $TabExtCnt" -ForegroundColor Yellow
  Write-Host "Pages: $PagCnt" -ForegroundColor Yellow
  Write-Host "Page Extensions: $PagExtCnt" -ForegroundColor Yellow
  Write-Host "Page Customizations: $PagCstmCnt" -ForegroundColor Yellow
  Write-Host "Reports: $RepCnt" -ForegroundColor Yellow
  Write-Host "Codeunits: $CodCnt" -ForegroundColor Yellow
  Write-Host "XMLPorts: $XMLCnt" -ForegroundColor Yellow
  Write-Host "Queries: $QueCnt" -ForegroundColor Yellow
  Write-Host "Enums: $EnumCnt" -ForegroundColor Yellow
  Write-Host "Enum Extensions: $EnumExtCnt" -ForegroundColor Yellow
  Write-Host "Keys: $KeysCnt" -ForegroundColor Yellow
  Write-Host "ControlAddin: $CtrlAddinCnt" -ForegroundColor Yellow
  Write-Host "Dotnet: $DotnetCnt" -ForegroundColor Yellow
  Write-Host "Profiles: $ProfileCnt" -ForegroundColor Yellow
   
 
  $sr.Close()
  $sr.Dispose()
  $sw.close()
  $sw.Dispose()
  Invoke-item "$WorkingFolder\SPLITBCOBJ\"
}
Split-ALObjectFile


AL-Pakete lassen sich so erzeugen (hier als Beispiel in C:\Temp)
Code:
$WorkingFolder = 'C:\Temp'
    Get-Content $WorkingFolder\*.al  -encoding UTF8 | Out-File $WorkingFolder\allobjects.al -encoding UTF8
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: AL-Objektsplitter

25. September 2019 15:36

Kowa hat geschrieben:Bei der Dateinamenstruktur für AL-Dateien kursieren ja mittlerweile verschiedene Varianten.

Auch innerhalb von MS. So erzeugt das Cmdlet Create-AlProjectFolderFromNavContainer aktuell die AL-Dateien, wenn man keine eigene Formatierung mitgibt.
ObjectNames.png
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: AL-Objektsplitter

16. Dezember 2021 19:03

Update mit Erkennung der neuen Object Types: Interface, Permission Set, Permission Set Extension und Entitlement.

Interfaces in AL
New AL objects – Entitlement, PermissionSet, and PermissionSetExtension

Code:
 function Split-ALObjectFile
    {
      $ErrorActionPreference = "Stop"
      $PSDefaultParameterValues['*:ErrorAction']='Stop'
      Function Get-FileName($initialDirectory)
      {
        [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
       
        $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
        $OpenFileDialog.initialDirectory = $initialDirectory
        $OpenFileDialog.filter = "AL Object Files (*.al)|*.al"
        $OpenFileDialog.ShowDialog() | Out-Null
        $OpenFileDialog.filename
      }
           
      $inputfile = Get-FileName "C:\temp" # This is the default path in OpenFile window.
      if ($inputfile -eq "") {throw 'Please select a file'}
         
      [decimal]$filesize = ((Get-Item $inputfile).length/1MB)
      $filesize =[math]::round($filesize,2)
      $inputfile = resolve-path $inputfile
      $WorkingFolder = Split-Path -Parent $inputfile
      if (Test-path "$WorkingFolder\SPLITBCOBJ\")
        {Remove-Item -path "$WorkingFolder\SPLITBCOBJ\" -Recurse -Force}


      Write-Host "Splitting BC/NAV objects from $inputfile (Size: $filesize MB) to $WorkingFolder\SPLITBCOBJ\, this may take a while..." -ForegroundColor Yellow
      $starttime = date
       
      $Sr = new-object System.IO.StreamReader($inputfile,[system.text.encoding]::GetEncoding(65001))
      [int]$ObjCnt = 0
      [int]$TabCnt = 0
      [int]$PagCnt = 0
      [int]$RepCnt = 0
      [int]$CodCnt = 0
      [int]$XmlCnt = 0
      [int]$QueCnt = 0
      [int]$TabExtCnt = 0
      [int]$PagExtCnt = 0
      [int]$EnumCnt = 0
      [int]$EnumExtCnt = 0
      [int]$CtrlAddInCnt = 0
      [int]$PagCstmCnt = 0
      [int]$KeysCnt = 0
      [int]$DotNetCnt = 0
      [int]$ProfileCnt = 0
      [int]$interfaceCnt = 0
      [int]$PermissionSetCnt = 0
      [int]$PermissionSetExtCnt = 0
      [int]$EntitlementCnt = 0

      while (-not $Sr.EndOfStream)
      {
        $Currline = $sr.ReadLine()
           
        if ($Currline.StartsWith('table ')) {$NewObject = $True}
        Elseif ($Currline.StartsWith('tableextension ')) {$NewObject = $True}
        Elseif  ($Currline.StartsWith('page ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('pageextension ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('pagecustomization ')) {$NewObject = $True}
        Elseif  ($Currline.StartsWith('report ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('query ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('codeunit ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('enum ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('enumextension ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('keys ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('profile ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('xmlport ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('dotnet ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('controladdin ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('interface ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('permissionset ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('permissionsetextension ')) {$NewObject = $True}
        Elseif   ($Currline.StartsWith('entitlement ')) {$NewObject = $True}
        else {$NewObject = $false}
             

           
        if ($NewObject)
        {
          $IsExtension = $false
          $dblquotes = [char]34
          [String]$ObjectChars = $Currline.Substring(0,5)
          [String]$ObjectChar = $Currline.Substring(6,1)
          if ($ObjectChars -eq 'permi')
            {$ObjectChars2 = $Currline.Substring(0,14)} else {$ObjectChars2 = ''}
         
             
          write-host $currline
          $IsExtension = ($Currline.indexof(' extends ') -gt 0)
          if ($IsExtension)
          {
            $PosExtends = $Currline.indexof(' extends ')
            if ($PosExtends -gt 0)
            {
              $ObjectExtendedName = $Currline.Substring($PosExtends+9)
              $CleanObjectExtendedName = $ObjectExtendedName.TrimStart('"')
              $CleanObjectExtendedName = $CleanObjectExtendedName.TrimEnd(' ')
              $CleanObjectExtendedName = $CleanObjectExtendedName.TrimEnd('"')
              $CleanObjectExtendedName = $CleanObjectExtendedName.Trim('/')     
            }
            else
            {$CleanObjectExtendedName = ''}
          }
          else
          {$CleanObjectExtendedName = ''}
             
          #write-host $ObjectChars
          #write-host $ObjectChar
          $PosFirstBlank =  $Currline.indexof(' ')
          $Currline2 = $Currline.Substring($PosFirstBlank+1)
          $PosSecondBlank =  $Currline2.indexof(' ')
          if ($PosSecondBlank -ne 0)
          {$ObjectName = $Currline2.Substring($PosSecondBlank+1)}
          else
          {$ObjectName = ''}
             
          if ($ObjectName.Startswith('"'))
          {
            $CleanObjectName = $ObjectName.TrimStart('"')
            $CleanObjectName = $CleanObjectName.TrimEnd(' ')
            $CleanObjectName = $CleanObjectName.TrimEnd('"')
            $CleanObjectName = $CleanObjectName.Trim('/')
          }
          else
          {$CleanObjectName = $ObjectName.Trim('/')}
             
          write-host $currline2
          $ObjectLine = $currline.Split(' ')
          $ObjCnt++
          Switch ($ObjectChars)
          {
            'table' {
              if (!$IsExtension)                 
              {
                $TabCnt++;$ObjectType = 'Table'
              }
              else
              {
                $TabExtCnt++;$ObjectType = 'TableExt'
              }
                             
            }
                 
            'page ' {
            $PagCnt++;$ObjectType = 'Page'}
     
            'pagee' {$PagExtCnt++; $ObjectType = 'PageExt'}
            'pagec' {$PagCstmCnt++;$ObjectType = 'PageCstm'}
            'repor' {$RepCnt++;$ObjectType = 'Report'}
            'codeu'{$CodCnt++;$ObjectType = 'Codeunit'}
            'xmlpo' {$XmlCnt++;$ObjectType = 'XMLport'}
            'enum ' {$enumCnt++;$ObjectType = 'Enum'}
            'enume' {$enumExtCnt++;$ObjectType = 'EnumExt'}
            'keys ' {$KeysCnt++;$ObjectType = 'Keys'}
            'profi' {$ProfileCnt++;$ObjectType = 'Profile'}
            'query' {$QueCnt++;$ObjectType = 'Query'}
            'dotne' {$DotNetCnt++;$ObjectType = 'DotNet'}
            'contr' {$CtrlAddinCnt++;$ObjectType = 'ControlAddin'}
            'inter' {$interfaceCnt++;$ObjectType = 'Interface'}
            'entit' {$EntitlementCnt++;$ObjectType = 'Entitlement'}

          }
          Switch ($ObjectChars2)
          { 
            'permissionset ' {$PermissionSetCnt++;$ObjectType = 'PermissionSet'}
            'permissionsete' {$PermissionSetExtCnt++;$ObjectType = 'PermissionSetExt'}
          }     
             
          if (!$IsExtension)
          {
            If (($ObjectLine[2] -eq '') -and ($CleanObjectName -eq ''))
                   
            {$ObjectFileName = $ObjectType + '.al'}
               
            elseif (($ObjectLine[1] -ne '') -and ($CleanObjectName -eq ''))
               
            {$ObjectFileName = $ObjectType + $ObjectLine[1] + '.al'}
               
            elseif (($ObjectLine[1] -ne '') -and ($CleanObjectName -eq ''))
               
            {$ObjectFileName = $ObjectType + $ObjectLine[1] + $CleanObjectName + '.al'}
               
            else
            {$ObjectFileName = $ObjectType + ' ' + $ObjectLine[1] + ' - ' + $CleanObjectName + '.al'}
          }
          else
          {
                 
                 
            $ObjectFileName = 'Modification  - ' + $CleanObjectExtendedName + '(' + $ObjectType + ')' + '.al'
          }
               
               
          #write-host "Object Type: $Objecttype"   
          #write-host "Object Name: $CleanObjectName"
          #write-host 'Object ID: ' $ObjectLine[1]
          #write-host "Object Extended Name: $CleanObjectExtendedName"
          $ObjectFileName = $ObjectFileName.replace('/','')
          write-host "File Name: $ObjectFileName"
          $Objectfile = New-Item -path "$WorkingFolder\SPLITBCOBJ\$ObjectFileName" -type file -force
               
          $sw = new-object System.IO.Streamwriter($Objectfile,$false,[system.text.encoding]::GetEncoding(65001))
          #IF (Test-Path $ObjectFile) {Remove-Item $ObjectFile}
           
        }
           
        if (-not $Currline.StartsWith('}'))
        {$sw.writeline($Currline)}
        else
        {
          $sw.writeline($Currline)
          $sw.writeline()
          $sw.Flush()

        }

           
      }
       
      $endtime = date
      $time = $endtime - $starttime
      Write-Host "$ObjCnt BC/NAV objects splitted to $WorkingFolder\SPLITBCOBJ\ in $($time.Minutes)m:$($time.Seconds)s:$($time.Milliseconds)ms" -ForegroundColor Yellow
      Write-Host "Tables: $TabCnt" -ForegroundColor Yellow
      Write-Host "Table Extensions: $TabExtCnt" -ForegroundColor Yellow
      Write-Host "Pages: $PagCnt" -ForegroundColor Yellow
      Write-Host "Page Extensions: $PagExtCnt" -ForegroundColor Yellow
      Write-Host "Page Customizations: $PagCstmCnt" -ForegroundColor Yellow
      Write-Host "Reports: $RepCnt" -ForegroundColor Yellow
      Write-Host "Codeunits: $CodCnt" -ForegroundColor Yellow
      Write-Host "XMLPorts: $XMLCnt" -ForegroundColor Yellow
      Write-Host "Queries: $QueCnt" -ForegroundColor Yellow
      Write-Host "Enums: $EnumCnt" -ForegroundColor Yellow
      Write-Host "Enum Extensions: $EnumExtCnt" -ForegroundColor Yellow
      Write-Host "Keys: $KeysCnt" -ForegroundColor Yellow
      Write-Host "ControlAddin: $CtrlAddinCnt" -ForegroundColor Yellow
      Write-Host "Dotnet: $DotnetCnt" -ForegroundColor Yellow
      Write-Host "Profiles: $ProfileCnt" -ForegroundColor Yellow
      Write-Host "Interfaces: $InterfaceCnt" -ForegroundColor Yellow
      Write-Host "Permission Sets: $PermissionSetCnt" -ForegroundColor Yellow
      Write-Host "Permission Set Extensions: $PermissionSetExtCnt" -ForegroundColor Yellow
      Write-Host "Entitlements: $EntitlementCnt" -ForegroundColor Yellow

       
     
      $sr.Close()
      $sr.Dispose()
      $sw.close()
      $sw.Dispose()
      Invoke-item "$WorkingFolder\SPLITBCOBJ\"
    }
    Split-ALObjectFile


ALfilesplitter.png
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.