OSDN Git Service

チケット #38687 tar.gz 形式が 7-ZIP32.DLL がインストールされている環境下で正しく展開されない
[applistation/AppliStation.git] / archive-inst / Program.cs
1 using System;\r
2 using System.Text;\r
3 using System.IO;\r
4 using Microsoft.Build.BuildEngine;\r
5 using NaGet.Packages;\r
6 using NaGet.Packages.Install;\r
7 \r
8 namespace ArchiveInstall\r
9 {\r
10         class Program\r
11         {\r
12                 public const string InstalledFileListName = ".applistation.installedfiles.xml";\r
13                 \r
14                 public const string InstalledPackageFileName = ".applistation.package.xml";\r
15                 \r
16                 private static void install(string fakeTargetDir, string targetDir)\r
17                 {\r
18                         // ハッシュ比較\r
19                         if (Directory.Exists(targetDir)) {\r
20                                 InstalledFileList list = null;\r
21                                 try {\r
22                                         list = NaGet.Utils.GetDeserializedObject<InstalledFileList>(Path.Combine(targetDir, InstalledFileListName));\r
23                                 } catch {\r
24                                 }\r
25                                 if (list != null) {\r
26                                         // 変更されたファイル(設定ファイル)をキープする処理\r
27                                         foreach (InstalledFile changedFile in list.Verify(targetDir)) {\r
28                                                 string changedFilePath = Path.Combine(targetDir, changedFile.Path);\r
29                                                 string toBeChangedFilePath = Path.Combine(fakeTargetDir, changedFile.Path);\r
30                                                 \r
31                                                 // 新規のファイルは退避させる\r
32                                                 if (File.Exists(toBeChangedFilePath)) {\r
33                                                         File.Move(toBeChangedFilePath, toBeChangedFilePath + ".newfile");\r
34                                                 }\r
35                                                 \r
36                                                 // 変更済みファイルをinstallSrcの方に反映させる\r
37                                                 if (File.Exists(changedFilePath)) {\r
38                                                         if (! Directory.Exists(Path.GetDirectoryName(toBeChangedFilePath))) {\r
39                                                                 // フォルダーがない場合は作る\r
40                                                                 Directory.CreateDirectory(Path.GetDirectoryName(toBeChangedFilePath));\r
41                                                         }\r
42                                                         File.Copy(changedFilePath, toBeChangedFilePath);\r
43                                                 }\r
44                                         }\r
45                                 }\r
46                         } else {\r
47                                 Directory.CreateDirectory(targetDir);\r
48                         }\r
49                         \r
50                         // まずはフォルダーツリーを作成\r
51                         foreach (string dir in Directory.GetDirectories(fakeTargetDir, "*", SearchOption.AllDirectories)) {\r
52                                 string dirPath = NaGet.Utils.GetRelativePath(fakeTargetDir, dir);\r
53                                 string targetDirPath = Path.Combine(targetDir, dirPath);\r
54                                 if (! Directory.Exists(targetDirPath)) {\r
55                                         Directory.CreateDirectory(targetDirPath);\r
56                                 }\r
57                         }\r
58                         \r
59                         // ファイルをインストール(高速化のため移動)\r
60                         foreach (string file in Directory.GetFiles(fakeTargetDir, "*", SearchOption.AllDirectories)) {\r
61                                 string filePath = NaGet.Utils.GetRelativePath(fakeTargetDir, file);\r
62                                 string targetFilePath = Path.Combine(targetDir, filePath);\r
63                                 if (File.Exists(targetFilePath)) {\r
64                                         try {\r
65                                                 File.SetAttributes(targetFilePath, FileAttributes.Normal);\r
66                                                 File.Delete(targetFilePath);\r
67                                         } catch (UnauthorizedAccessException e) {\r
68                                                 throw new IOException(string.Format("Access denied '{0}'", targetFilePath), e);\r
69                                         }\r
70                                 }\r
71                                 File.Move(file, targetFilePath);\r
72                         }\r
73                 }\r
74                 \r
75                 private static void _installDllTo(string sys32path, string srcDir, Package package)\r
76                 {\r
77                         if (!string.IsNullOrEmpty(package.System32CopyFiles)) {\r
78                                 if (! Directory.Exists(sys32path)) {\r
79                                         Directory.CreateDirectory(sys32path);\r
80                                 }\r
81                                 \r
82                                 string logfile = Path.Combine(sys32path, ".applistation.install."+package.Name+".log");\r
83                                 if (File.Exists(logfile)) {\r
84                                         File.SetAttributes(logfile, FileAttributes.Normal);\r
85                                 }\r
86                                 using (FileStream fs = new FileStream(logfile, FileMode.Create))\r
87                                 using (StreamWriter sw = new StreamWriter(fs)){\r
88                                         foreach (string pathpat in package.System32CopyFiles.Split(';')) {\r
89                                                 foreach (string path in NaGet.Utils.ExtendWildcardFile(srcDir, pathpat)) {\r
90                                                         if ((File.GetAttributes(path) & FileAttributes.Directory) == 0) { // もしファイルならば\r
91                                                                 string destPath = Path.Combine(sys32path, Path.GetFileName(path));\r
92                                                                 File.Copy(path, destPath, true);\r
93                                                                 \r
94                                                                 sw.WriteLine(Path.GetFileName(destPath));\r
95                                                         }\r
96                                                 }\r
97                                         }\r
98                                 }\r
99                                 File.SetAttributes(logfile, FileAttributes.Hidden);\r
100                         }\r
101                 }\r
102                 \r
103                 private static void postInstall(string targetDir, Package package)\r
104                 {\r
105                         // SYSTEM32へのコピーの実行\r
106                         _installDllTo(NaGet.Env.ArchiveSystem32, targetDir, package);\r
107                         \r
108                         // インストールスクリプトの実行\r
109                         if (! string.IsNullOrEmpty(package.InstallScript) ) {\r
110                                 Engine engine = MSBuild.Engine;\r
111                                 Project proj = new Project(engine);\r
112                                 try {\r
113                                         proj.LoadXml(package.InstallScript);\r
114                                 } catch (InvalidProjectFileException e) {\r
115                                         throw new ApplicationException("InstallScript is invalid", e);\r
116                                 }\r
117                                 \r
118                                 engine.BuildProject(proj, "Install");\r
119                         }\r
120                         \r
121                         // 直下のファイルで*.exeがGUIプログラムならプログラムフォルダーに登録\r
122                         foreach (string exeFile in Directory.GetFiles(targetDir, "*.exe")) {\r
123                                 if (NaGet.InteropServices.PEFileInfoUtils.GetPEFileType(exeFile) == NaGet.InteropServices.PEFileType.WinGUI) {\r
124                                         string progGrpPath = Path.Combine(NaGet.Env.ArchiveProgramGroup, package.Name);\r
125                                         if (! Directory.Exists(progGrpPath)) Directory.CreateDirectory(progGrpPath);\r
126                                         \r
127                                         using (NaGet.InteropServices.ShellLink lnk = new NaGet.InteropServices.ShellLink() ) {\r
128                                                 //string path = NaGet.Utils.GetRelativePath(progGrpPath, exeFile);// lnkファイルに相対パス指定不能\r
129                                                 string path = exeFile;\r
130                                                 \r
131                                                 lnk.Path = path;\r
132                                                 //lnk.SetIconLocation(path, 0);\r
133                                                 \r
134                                                 // .lnk ファイル名\r
135                                                 string lnkFilePath = Path.Combine(progGrpPath, lnk.GetSuitableShellLinkNameFor() + ".lnk");\r
136                                                 if (File.Exists(lnkFilePath)) { // ファイル名がかぶってしまったとき\r
137                                                         lnkFilePath = Path.Combine(progGrpPath, Path.GetFileNameWithoutExtension(exeFile) + ".lnk");\r
138                                                 }\r
139                                                 \r
140                                                 // 保存\r
141                                                 lnk.ToPersistFile().Save(lnkFilePath, true);\r
142                                         }\r
143                                 }\r
144                         }\r
145                 }\r
146                 \r
147                 private static void storeInstalledFileList(string targetDir)\r
148                 {\r
149                         string installedFileListPath = Path.Combine(targetDir, InstalledFileListName);\r
150                         if (File.Exists(installedFileListPath)) {\r
151                                 File.SetAttributes(installedFileListPath, FileAttributes.Normal);\r
152                         }\r
153                         InstalledFileList installedFileList = InstalledFileList.CreateFromFiles(targetDir);\r
154                         NaGet.Utils.PutSerializeObject(installedFileListPath, installedFileList);\r
155                         \r
156                         File.SetAttributes(installedFileListPath, FileAttributes.Hidden | FileAttributes.ReadOnly);\r
157                 }\r
158                 \r
159                 private static void storePackageXml(Package package, string destDir)\r
160                 {\r
161                         if (package == null) {\r
162                                 return;\r
163                         }\r
164                         \r
165                         InstalledPackage pkg = InstalledPackage.PackageConverter(package);\r
166                         UninstallInformation uninfo = pkg.UninstallInfo;\r
167                         uninfo.InstallLocation = destDir;\r
168                         uninfo.UninstallString = string.Format("archive-inst -x \"{0}\"", package.Name);\r
169                         uninfo.EstimatedSize = (int) (NaGet.Utils.GetFileSize(destDir) >> 10);\r
170                         uninfo.InstallDateString = DateTime.Now.ToString("yyyyMMdd");\r
171                         pkg.UninstallInfo = uninfo;\r
172                         \r
173                         string packageXmlFilePath = Path.Combine(destDir, InstalledPackageFileName);\r
174                         if (File.Exists(packageXmlFilePath)) {\r
175                                 File.SetAttributes(packageXmlFilePath, FileAttributes.Normal);\r
176                         }\r
177                         NaGet.Utils.PutSerializeObject(packageXmlFilePath, pkg);\r
178                         File.SetAttributes(packageXmlFilePath, FileAttributes.Hidden | FileAttributes.ReadOnly);\r
179                 }\r
180                 \r
181                 private static void _removeDllFrom(string sys32path, InstalledPackage package)\r
182                 {\r
183                         if (! string.IsNullOrEmpty(package.System32CopyFiles) ) {\r
184                                 string logfile = Path.Combine(sys32path, ".applistation.install."+package.Name+".log");\r
185                                 \r
186                                 if (File.Exists(logfile)) {\r
187                                         using (FileStream fs = new FileStream(logfile, FileMode.Open))\r
188                                         using (StreamReader sr = new StreamReader(fs)){\r
189                                                 string fileName = sr.ReadLine().Trim();\r
190                                                 string filePath = Path.Combine(sys32path, fileName);\r
191                                                 \r
192                                                 if (File.Exists(filePath)) {\r
193                                                         File.SetAttributes(filePath, FileAttributes.Normal);\r
194                                                         File.Delete(filePath);\r
195                                                 }\r
196                                         }\r
197                                         File.SetAttributes(logfile, FileAttributes.Normal);\r
198                                         File.Delete(logfile);\r
199                                 }\r
200                         }\r
201                 }\r
202                 \r
203                 private static void removePackage(InstalledPackage package, string targetDir)\r
204                 {\r
205                         // アンインストールスクリプトの実行\r
206                         if (! string.IsNullOrEmpty(package.InstallScript)) {\r
207                                 Engine engine = MSBuild.Engine;\r
208                                 Project proj = new Project(engine);\r
209                                 try {\r
210                                         proj.LoadXml(package.InstallScript);\r
211                                 } catch (InvalidProjectFileException e) {\r
212                                         throw new ApplicationException("InstallScript is invalid", e);\r
213                                 }\r
214                                 \r
215                                 engine.BuildProject(proj, "Uninstall");\r
216                         }\r
217                         \r
218                         // GUIプログラムでプログラムフォルダーに登録しているのを解除\r
219                         string progGrpPath = Path.Combine(NaGet.Env.ArchiveProgramGroup, package.Name);\r
220                         if (Directory.Exists(progGrpPath)) {\r
221                                 NaGet.Utils.SetAttributeRecursive(progGrpPath, FileAttributes.Normal);\r
222                                 Directory.Delete(progGrpPath, true);\r
223                         }\r
224                         \r
225                         // SYSTEM32からの削除の実行\r
226                         _removeDllFrom(NaGet.Env.ArchiveSystem32, package);\r
227                         \r
228                         try {\r
229                                 NaGet.Utils.SetAttributeRecursive(targetDir, FileAttributes.Normal);\r
230                                 Directory.Delete(targetDir, true);\r
231                         } catch (UnauthorizedAccessException) {\r
232                                 Console.Error.WriteLine("W: could not complete to delete '{0}': Permission denied", targetDir);\r
233                                 Environment.Exit(1);\r
234                         }\r
235                 }\r
236                 \r
237                 private static void parseMainArguments(string[] args, out string arcFile, out string targetDir, out Package package)\r
238                 {\r
239                         if (args.Length < 1) {\r
240                                 throw new ArgumentException();\r
241                         }\r
242                         \r
243                         switch (args[0].ToLower()) {\r
244                                 case "-t":\r
245                                         if (args.Length != 3) {\r
246                                                 throw new ArgumentException();\r
247                                         }\r
248                                         \r
249                                         arcFile = args[1];\r
250                                         targetDir = args[2];\r
251                                         package = null;\r
252                                         break;\r
253                                 case "-i":\r
254                                         if (args.Length != 3) {\r
255                                                 throw new ArgumentException();\r
256                                         }\r
257                                         \r
258                                         arcFile = args[1];\r
259                                         PackageList<Package> pkgList = NaGet.Utils.GetDeserializedObject<PackageList<Package>>(NaGet.Env.PackageListFile);\r
260                                         package = pkgList.GetPackageForName(args[2]);\r
261                                         targetDir = Path.Combine(NaGet.Env.ArchiveProgramFiles, package.Name);\r
262                                         break;\r
263                                 case "-x":\r
264                                         if (args.Length != 2) {\r
265                                                 throw new ArgumentException();\r
266                                         }\r
267                                         \r
268                                         arcFile = null;\r
269                                         targetDir = Path.Combine(NaGet.Env.ArchiveProgramFiles, args[1]);\r
270                                         package = null;\r
271                                         \r
272                                         string filepath = Path.Combine(targetDir, InstalledPackageFileName);\r
273                                         if (File.Exists(filepath)) {\r
274                                                 package = NaGet.Utils.GetDeserializedObject<InstalledPackage>(filepath);\r
275                                         } else {\r
276                                                 Console.Error.WriteLine("Not found or already removed package : {0}", args[1]);\r
277                                                 Environment.Exit(100);\r
278                                         }\r
279                                         break;\r
280                                 default:\r
281                                         arcFile = null;\r
282                                         targetDir = null;\r
283                                         package = null;\r
284                                         Console.Error.WriteLine("Unreconized command \"{0}\".", args[0]);\r
285                                         Environment.Exit(100);\r
286                                         break;\r
287                         }\r
288                 }\r
289                 \r
290                 [STAThread]\r
291                 public static void Main(string[] args)\r
292                 {\r
293                         // アーカイブSYSTEM32をパスに足す\r
294                         NaGet.Utils.AddDirectoryToPath(NaGet.Env.ArchiveSystem32);\r
295                         \r
296                         string arcFile = null;\r
297                         string targetDir = null;\r
298                         Package package = null;\r
299                         \r
300                         // 引数パースおよびヘルプ表示\r
301                         try {\r
302                                 parseMainArguments(args, out arcFile, out targetDir, out package);\r
303                         } catch (ArgumentException) {\r
304                                 string executeFileName = System.AppDomain.CurrentDomain.FriendlyName;\r
305                                 Console.Write("Usage:");\r
306                                 Console.WriteLine("\t{0} -t archive.zip target_dir\tExtraction", executeFileName);\r
307                                 Console.WriteLine("\t{0} -i archive.zip PackageName\tInstall", executeFileName);\r
308                                 Console.WriteLine("\t{0} -x PackageName\t\tUninstall", executeFileName);\r
309                                 Console.WriteLine();\r
310                                 \r
311                                 Environment.ExitCode = 1;\r
312                         }\r
313 \r
314                         // インストールおよび展開処理\r
315                         if (arcFile != null) {\r
316                                 string tempExtractDir = targetDir + "___temp___"; // HACK\r
317                                 Directory.CreateDirectory(tempExtractDir);\r
318                                 \r
319                                 try {\r
320                                         // STEP1. 書庫の展開\r
321                                         if (package != null && package.Type == InstallerType.ITSELF) {\r
322                                                 // 書庫でない場合展開せずにそのままコピーする\r
323                                                 string destFile = Path.Combine(tempExtractDir, Path.GetFileName(arcFile));\r
324                                                 File.Copy(arcFile, destFile);\r
325                                         } else {\r
326                                                 NaGet.ArchiveExtractionHelpers.ArchiveExtract.Extract(arcFile, tempExtractDir);\r
327                                         }\r
328                                         \r
329                                         // インストールの元となるフォルダを決定する\r
330                                         string installSrc = tempExtractDir;\r
331                                         // 展開先のトップに唯一のフォルダーしかないのであれば、そのフォルダー内を基準とする\r
332                                         if (Directory.GetFiles(tempExtractDir).Length == 0 &&\r
333                                             Directory.GetDirectories(tempExtractDir).Length == 1) {\r
334                                                 installSrc = Directory.GetDirectories(tempExtractDir)[0];\r
335                                         }\r
336                                         \r
337                                         // STEP2. インストール元フォルダでインストールファイルリストの作成\r
338                                         storeInstalledFileList(installSrc);\r
339                                         \r
340                                         // STEP3. インストール\r
341                                         install(installSrc, targetDir);\r
342                                         \r
343                                         if (package != null) {\r
344                                                 // STEP4. カスタマイズ可能な後処理\r
345                                                 postInstall(targetDir, package);\r
346                                                 \r
347                                                 // STEP5. パッケージ情報をインストール先(targetDir)に置く\r
348                                                 storePackageXml(package, targetDir);\r
349                                         }\r
350                                 } catch (NaGet.InteropServices.CommonArchiverDllExtractionException e) {\r
351                                         Console.Error.WriteLine("E: Error: {0}", e.Message);\r
352                                         Environment.ExitCode = e.ReturnValue;\r
353                                 } catch (NaGet.ArchiveExtractionHelpers.SevenZipExtractException e) {\r
354                                         Console.Error.WriteLine("E: Error: {0}", e.Message);\r
355                                         Environment.ExitCode = e.ReturnValue;\r
356                                 } catch (ApplicationException e) {\r
357                                         Console.Error.WriteLine("E: Error: {0}", e.Message);\r
358                                         Environment.ExitCode = 1;\r
359                                 } catch (IOException e) {\r
360                                         Console.Error.WriteLine("E: File I/O Error : {0}", e.Message);\r
361                                         Environment.ExitCode = 1;\r
362                                 } finally {\r
363                                         Directory.Delete(tempExtractDir, true);\r
364                                 }\r
365                         } else if (package is InstalledPackage) {\r
366                                 removePackage((InstalledPackage) package, targetDir);\r
367                         }\r
368                 }\r
369         }\r
370 }\r