Quello che segue è uno script che usavo per generare in automatico il file .DDF e manifest.xml necessari per creare i file di solution (.WSP) di SharePoint 2007 (WSS3 - MOSS). Completa anche le infirmazioni mancanti nei file Feature.xml che vanno valorizzati con ID, nome feature, scope, ecc...
Lo script per funzionare richiede che i file siano in una struttura di cartelle che rispecchia quella di SharePoint, come la seguente:
+- 12
|   +- CONFIG
|   +- TEMPLATE
|        +- CONTROLTEMPLATES
|        +- FEATURES
|        |   +- MiaFeature1
|        |   |   +- Feature.xml
|        |   |   +- eventuali altri file
|        |   +- MiaFeature2
|        +- IMAGES
|        +- SiteTemplates
|        +- CONTROLTEMPLATES
|        +- THEMES
+- DLL
+- Solution
|   +- solution.xml
+- script.ps1

La prima cosa da fare è creare il file solution.xml con i corretti nomi degli assembly (andrebbe automatizzata anche questa parte)
<?xml version="1.0" encoding="utf-8" ?>
<Solution Id="45C275D7-8475-4b05-BD17-B0A02DDF4343"
		  FileName="BdcPages.wsp">
	<Assemblies>

		<Assembly Location="BdcHelper.dll"
				  DeploymentTarget="GlobalAssemblyCache" 
				  Namespace="BdcHelper" />

		<Assembly Location="BDCDataLayer.dll"
				  DeploymentTarget="GlobalAssemblyCache" 
				  Namespace="BDCDataLayer" />
	</Assemblies>

</Solution> 

successivamente si può lanciare lo script PowerShell 1.0 (makesolution.ps1)
#genera i file di solution
# solution.ddf
# manifest.xml
# *\features.xml
#basandosi sul file solution\solution.xml che va impostato
#con il corretto SolutionId e nome file WSP
#v. 1.0 26-06-2008

################################################################
# Functions
function MyWrite ($msg){
	Write-Host $msg  -ForegroundColor green;
}
function MyWriteF ($msg){
	Write-Host "Write file: $msg"  -ForegroundColor Gray;
}
################################################################

$dt = date;
MyWrite "Begin: $dt";

########################################################################
# nome file wsp
MyWrite "Read configuration";
[xml]$solutionXML = Get-Content "solution.xml";
$solutionId = $solutionXML.Solution.Id;
$solutionWSP = $solutionXML.Solution.FileName;
MyWrite "Solution Id:  $solutionId";
MyWrite "File wsp:  $solutionWSP";
		  
########################################################################
# costanti percorsi
$root = "..";
$hiveControls = "$root\12\TEMPLATE\CONTROLTEMPLATES"
$hiveFeatures = "$root\12\TEMPLATE\FEATURES"
$hiveImages = "$root\12\TEMPLATE\IMAGES"
$hiveLayouts = "$root\12\TEMPLATE\LAYOUTS"
$hiveSiteTemplates = "$root\12\TEMPLATE\SiteTemplates"
$hiveSiteThemes = "$root\12\TEMPLATE\THEMES"
$hiveConfig = "$root\12\CONFIG"

$hiveDLL = "$root\DLL";
$hiveSolution = "$root\Solution";

$fileSolution = "";
$fileManifest = "";

########################################################################
# Assemblies
MyWrite "Make: Assemblies";

$fileManifest += "`r`n<Assemblies>`r`n";

$fileSolution += ";---------------------------------------------`r`n;Assemblies`r`n";
$fileManifest += "  <!-- Assemblies -->`r`n";
$path = $hiveDLL;

$solutionXML.Solution.Assemblies.Assembly | ForEach-Object -Process {

	$fileName = $_.Location;
	$deploy = $_.DeploymentTarget;
	$namespace = $_.Namespace
	$file = Get-Item "$path\$fileName"
	$dll = [System.reflection.assembly]::ReflectionOnlyLoadFrom($file.FullName);
	$fullName = $dll.FullName;

	$fileManifest += " 
		<Assembly Location=`"$fileName`" DeploymentTarget=`"$deploy`" >
			<SafeControls>
				<SafeControl Assembly=`"$fullName`"
							 Namespace=`"$namespace`"
							 TypeName=`"*`"
							 Safe=`"True`" />
			</SafeControls>
		</Assembly>
	";
	$fileSolution += "`"$hiveDLL\$fileName`" $fileName`r`n";
	$dll = $null;
}

$fileManifest += "</Assemblies>`r`n";

########################################################################
# FEATURE 
MyWrite "Make: Features";

$fileManifest += "`r`n<FeatureManifests>`r`n";
$path = $hiveFeatures
$pathLen = 0;
$pathFrom = "";
$pathTo = "";
#processo tutte le artelle feature
$items = Get-ChildItem "$path\*" | where { $_.Mode.StartsWith("d") }
$items | ForEach-Object -Process {
	$file = $_;
	$pathLen =  $file.FullName.Length+1;
	$dirName = $file.name;
	$pathFrom = "$path\$dirName";
	$pathTo = $dirName;
	MyWrite " $dirName";
	$fileManifest += "  <!-- $dirName -->`r`n";
	$fileSolution += ";---------------------------------------------`r`n;$dirName`r`n";
	
	#processo il contenuto della feature

	#controllo se esiste il file feature.xml
	$ft =  Get-Item "$file\feature.xml";
	if($ft){
		#ok feature.xml esiste
		[xml]$featureXml = Get-Content $ft.FullName;
		$nodeEM = $featureXml.Feature.ElementManifests;
		if ($nodeEM) {
			$nodeEM.RemoveAll();
		
			$fileManifest += "  <FeatureManifest Location=`"$dirName\$($ft.Name)`" />`r`n";
			$fileSolution += "$hiveFeatures\$dirName\$($ft.Name) $dirName\$($ft.Name)`r`n";

			Get-ChildItem "$file\*" -Recurse -Exclude *.cs | where {  $_.Mode.StartsWith("-") } |  ForEach-Object -Process {
				$file = $_;
				$dirName = $file.directory.name;
				$fileName = $file.name;
				$element ="";
				$location = $file.FullName.Substring($pathLen);
				[xml]$node = $null;
				
				$skip = $false;
				if($file.Extension.ToLower().Equals(".xml")) {
					[xml]$xml = Get-Content $_.FullName;
					if($xml.Feature) {
						#feature.xml --- non faccio niente
						$element = "";
						$skip = $true;
					} elseif($xml.Elements) {
						#file elements
						$element = "ElementManifest";
						$skip = $true;
					}
				}
				
				if ($skip -eq $false) {
					$element = "ElementFile";
				}
				if($element.Equals("") -eq $false){
					$nd = $featureXML.CreateElement($element, "http://schemas.microsoft.com/sharepoint/");
					$nd.SetAttribute("Location", $location);
					[void]$nodeEM.AppendChild($nd);
					$fileSolution += "`"$pathFrom\$location`"  $pathTo\$location`r`n";
				}
			}
			$fNew = $ft.FullName;
			MyWriteF "$($ft.Directory.Name)\$($ft.Name)";
			attrib -R $fNew;
			$featureXml.save($fNew);
		}
	}
}
$fileManifest += "</FeatureManifests>`r`n";


########################################################################
# TemplateFiles
$fileManifest += "`r`n<TemplateFiles>`r`n";

MyWrite "Make: CONTROLTEMPLATES";

$fileSolution += ";---------------------------------------------`r`n;CONTROLTEMPLATES`r`n";
$fileManifest += "  <!-- CONTROLTEMPLATES -->`r`n";
$path = $hiveControls
$pathLen = (Get-Item $path).FullName.Length+1;
$pathFrom = $path;
$pathTo = "";
Get-ChildItem "$path\*" -Recurse -Exclude *.cs | where {  $_.Extension.ToLower().Equals(".ascx") } |  ForEach-Object -Process {
	$dirName = $_.directory.name;
	$fileName = $_.name;
	$pathTo = "CONTROLTEMPLATES";
	$location = "$($_.FullName.Substring($pathLen))";
	
	$fileManifest += "  <TemplateFile Location=`"$location`" />`r`n";
	$fileSolution += "$pathFrom\$location $pathTo\$location`r`n";
}

MyWrite "Make: IMAGES";

$fileSolution += ";---------------------------------------------`r`n;IMAGES`r`n";
$fileManifest += "  <!-- IMAGES -->`r`n";
$path = $hiveImages
$pathLen = (Get-Item $path).FullName.Length+1;
$pathFrom = $path;
Get-ChildItem "$path\*" -Recurse -Exclude *.cs | ForEach-Object -Process {
	
	$dirName = $_.directory.name;
	$fileName = $_.name;
	$pathTo = "IMAGES";
	$location = "$($_.FullName.Substring($pathLen))";
	
	$fileManifest += "  <TemplateFile Location=`"$location`" />`r`n";
	$fileSolution += "$pathFrom\$location $pathTo\$location`r`n";
}

MyWrite "Make: LAYOUTS";

$fileSolution += ";---------------------------------------------`r`n;LAYOUTS`r`n";
$fileManifest += "  <!-- LAYOUTS -->`r`n";
$path = $hiveLayouts;
$pathLen = (Get-Item $path).FullName.Length+1;
$pathFrom = $path;
Get-ChildItem "$path\*" -Recurse -Exclude *.cs | ForEach-Object -Process {
	
	$dirName = $_.directory.name;
	$fileName = $_.name;
	$pathTo = "LAYOUTS";
	$location = "$($_.FullName.Substring($pathLen))";
	
	$fileManifest += "  <TemplateFile Location=`"$location`" />`r`n";
	$fileSolution += "$pathFrom\$location $pathTo\$location`r`n";
}

$fileManifest += "</TemplateFiles>`r`n";


########################################################################
# TemplateFiles
$fileManifest += "`r`n<RootFiles>`r`n";
	
MyWrite "Make: CONFIG";

$fileSolution += ";---------------------------------------------`r`n;CONFIG`r`n";
$fileManifest += "  <!-- CONFIG -->`r`n";
$path = $hiveConfig;
$pathLen = (Get-Item $path).FullName.Length+1;
$pathFrom = $path;
Get-ChildItem "$path\*" -Recurse -Exclude *.cs | ForEach-Object -Process {
	
	$dirName = $_.directory.name;
	$fileName = $_.name;
	$pathTo = "CONFIG";
	$location = "$($_.FullName.Substring($pathLen))";
	
	$fileManifest += "  <RootFile Location=`"$location`" />`r`n";
	$fileSolution += "$pathFrom\$location $pathTo\$location`r`n";
}

$fileManifest += "</RootFiles>`r`n";



################################################################
# output manifest.xml
MyWriteF "manifest.xml";

attrib -R "$hiveSolution\manifest.xml";

"<!-- file: $solutionWSP - Date: $dt-->
<Solution SolutionId=`"$solutionId`" xmlns=`"http://schemas.microsoft.com/sharepoint/`" >
$fileManifest
</Solution>" > "$hiveSolution\manifest.xml";

################################################################
# output solution.ddf
MyWriteF "solution.ddf";

attrib -R "$hiveSolution\solution.ddf";

";file: $solutionWSP - Date: $dt
.OPTION EXPLICIT
.Set CabinetNameTemplate=$solutionWSP
.set DiskDirectoryTemplate=CDROM
.Set CompressionType=MSZIP
.Set UniqueFiles=`"On`"
.Set Cabinet=on
.Set DiskDirectory1=

solution\manifest.xml manifest.xml

$fileSolution
" > "$hiveSolution\solution.ddf";
$dt = date;
MyWrite "End: $dt";

In pratica lo script scorre tutte le cartelle sotto la 12 e genera le corrette entry nei file manifest.xml, solution.ddf e nei vari feature.xml.

Per generare il pacchetto solution (.WSP)
c:\windows\system32\makecab.exe /F solution.ddf
Per l'installazione delle solution e approfondimenti, vedi anche:
Come creare un file di solution (.wsp)
Microsoft SharePoint Solution Installer
Lo script non gestisce tutte le casistiche è può sicuramente essere milgliorato, in ogni caso si possono trovare vari spunti sull'uso di PowerShell con XML