3 using System.Diagnostics;
\r
5 using NaGet.SubCommands;
\r
7 namespace NaGet.Packages.Install
\r
11 /// ダウンロード・インストール処理をカプセル化するクラス
\r
13 public class Installation
\r
18 public Package InstalledPackage;
\r
21 /// (保存される)インストーラのファイルのパス
\r
23 public string InstallerFile;
\r
26 /// インストールが完了されたか否かのフラグ
\r
28 private bool installed = false;
\r
31 /// 起動するインストーラのインデックス番号
\r
33 protected int installerIndex = 0;
\r
36 /// 外部アプリのエラー出力の受信ハンドラ
\r
38 public event EventHandler<NaGet.Utils.AnyDataEventArgs<string>> ErrorDataReceived;
\r
41 /// 外部アプリの標準出力の受信ハンドラ
\r
43 public event EventHandler<NaGet.Utils.AnyDataEventArgs<string>> OutputDataReceived;
\r
48 /// <param name="package">インストールするパッケージ</param>
\r
49 public Installation(Package package)
\r
51 InstalledPackage = package;
\r
52 InstallerFile = getArchiveFilePath();
\r
53 installerIndex = GetPreferInstallerIndex(package);
\r
59 /// <param name="package">インストールするパッケージ</param>
\r
60 /// <param name="installerfile">(保存される)インストーラのファイルのパス</param>
\r
61 protected Installation(Package package, string installerfile)
\r
63 InstalledPackage = package;
\r
64 InstallerFile = installerfile;
\r
65 installerIndex = GetPreferInstallerIndex(package);
\r
71 public bool IsInstallablePackage()
\r
73 return installerIndex >= 0;
\r
77 /// すでにインストールされているパッケージを取得する
\r
79 /// <param name="installedPkgs">
\r
83 /// インストールされているパッケージの情報。インストールされたパッケージが見つからないならばnullを返す
\r
85 public InstalledPackage GetInstalledPackage(PackageList<InstalledPackage> installedPkgs)
\r
87 return installedPkgs.GetPackageForName(InstalledPackage.Name);
\r
93 /// <param name="downloader">ダウンローダオブジェクト</param>
\r
94 public void Download(Downloader downloader)
\r
97 string url = InstalledPackage.Installer[installerIndex].Url.Href;
\r
98 downloader.Download(url, InstallerFile);
\r
100 // サーバ指定のファイル名に変更する
\r
101 if (! string.IsNullOrEmpty(downloader.DownloadedFileName)) {
\r
102 string newFile = Path.Combine(Path.GetDirectoryName(InstallerFile), downloader.DownloadedFileName);
\r
103 File.Move(InstallerFile, newFile);
\r
104 InstallerFile = newFile;
\r
110 /// ハッシュ検証のためのハッシュの種類の数を返す
\r
112 /// <returns>ハッシュの個数</returns>
\r
113 public int GetRegisteredHashCount()
\r
115 HashValue[] hashValues = InstalledPackage.Installer[installerIndex].Hash;
\r
116 return (hashValues == null)? 0 : hashValues.Length;
\r
122 /// <returns>検証にパスしたらtrue、パスしなかったらfalse</returns>
\r
123 public bool VerifyHashValues()
\r
125 HashValue[] hashValues = InstalledPackage.Installer[installerIndex].Hash;
\r
126 if (hashValues != null) {
\r
127 foreach (HashValue hash in hashValues) {
\r
128 if (! hash.Validate(InstallerFile)) {
\r
137 private int invokeInstaller(string installerfile, InstallerType type)
\r
139 if (! File.Exists(installerfile)) {
\r
140 throw new FileNotFoundException(string.Format("{0} is not found, perhaps failed to download.", installerfile), installerfile);
\r
143 Process hProcess = null;
\r
146 case InstallerType.EXEC_INSTALLER:
\r
147 hProcess = Process.Start(installerfile);
\r
148 hProcess.WaitForExit();
\r
151 case InstallerType.MSI_PACKAGE:
\r
152 hProcess = Process.Start("msiexec", string.Format("/i \"{0}\"", installerfile));
\r
153 hProcess.WaitForExit();
\r
155 case InstallerType.ARCHIVE:
\r
157 if (File.Exists("archive-inst.exe")) {
\r
158 string argument = string.Format("-i \"{0}\" \"{1}\"", installerfile, this.InstalledPackage.Name);
\r
159 ProcessStartInfo procInfo = new ProcessStartInfo(Path.GetFullPath("archive-inst.exe"), argument);
\r
160 procInfo.UseShellExecute = false;
\r
161 procInfo.CreateNoWindow = true;
\r
162 procInfo.WorkingDirectory = Environment.CurrentDirectory;
\r
164 hProcess = NaGet.Utils.ProcessStartWithOutputCapture(procInfo, this.OutputDataReceived, this.ErrorDataReceived);
\r
165 hProcess.WaitForExit();
\r
168 throw new NotImplementedException("Not implemented archive installation yet");
\r
171 throw new NotImplementedException("Not implemented archive installation yet");
\r
174 return hProcess.ExitCode;
\r
176 if (hProcess != null) {
\r
183 /// インストーラ等を起動してインストール作業を行う
\r
185 /// <returns>インストーラの終了コード</returns>
\r
186 public int Install()
\r
188 string installerFile = this.InstallerFile;
\r
189 string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
\r
191 // アーカイブされているなら一旦展開
\r
192 if (InstalledPackage.ArchivedInstaller) {
\r
193 Directory.CreateDirectory(tempDir);
\r
195 // 64bit対策で展開も別プロセスで
\r
196 // TODO エラーとかの出力を取得できたりとかできるように
\r
197 if (File.Exists("archive-inst.exe")) {
\r
198 string argument = string.Format("-t \"{0}\" \"{1}\"", installerFile, tempDir);
\r
199 ProcessStartInfo info = new ProcessStartInfo(Path.GetFullPath("archive-inst.exe"), argument);
\r
200 info.UseShellExecute = false;
\r
201 info.WorkingDirectory = Environment.CurrentDirectory;
\r
203 using (Process hProcess = Process.Start(info)) {
\r
204 hProcess.WaitForExit();
\r
206 if (hProcess.ExitCode != 0) {
\r
207 throw new ApplicationException("Extract error " + installerFile + " to " + tempDir);
\r
211 throw new ApplicationException(string.Format("archive-inst.exe not found in {0}!", Environment.CurrentDirectory));
\r
214 // System.Text.StringBuilder output = new System.Text.StringBuilder(1024);
\r
215 // int res = NaGet.InteropServices.CommonArchiverExtracter.ExtractArchive(installerFile, tempDir, output, IntPtr.Zero);
\r
217 // throw new ApplicationException("Extract error\n" + output.ToString());
\r
220 installerFile = seekInstallerFile(tempDir, InstalledPackage.Type);
\r
221 if (installerFile == null && Directory.GetDirectories(tempDir).Length == 1) {
\r
222 installerFile = seekInstallerFile(Path.Combine(tempDir, Directory.GetDirectories(tempDir)[0]), InstalledPackage.Type);
\r
227 int exitCode = invokeInstaller(installerFile, InstalledPackage.Type);
\r
237 public bool Downloaded {
\r
239 return File.Exists(InstallerFile) && ((File.GetAttributes(InstallerFile) & FileAttributes.Hidden) != FileAttributes.Hidden);
\r
244 /// インストール作業済みであるか否か。実際にインストールされたかどうかではありません。
\r
246 public bool Installed {
\r
247 get { return installed; }
\r
251 /// インストーラの処理が成功してインストールされたプログラムが確認できるか否か。
\r
253 public bool InstallSuccessed {
\r
256 case InstallerType.ARCHIVE: // アーカイブインストーラはフォルダの確認
\r
257 return Directory.Exists(Path.Combine(NaGet.Env.ArchiveProgramFiles, InstalledPackage.Name));
\r
258 case InstallerType.EXEC_INSTALLER:
\r
259 case InstallerType.MSI_PACKAGE:
\r
260 return RegistriedUninstallers.GetInstalledPackageFor(InstalledPackage) != null;
\r
270 public InstallerType Type {
\r
272 return InstalledPackage.Type;
\r
277 /// もっともふさわしいインストーラ番号を返す
\r
279 /// <returns></returns>
\r
280 public static int GetPreferInstallerIndex(Package InstalledPackage)
\r
282 if (InstalledPackage.Type == InstallerType.CANNOT_INSTALL) { // インストール不能パッケージ
\r
289 for (int i = 0; i < InstalledPackage.Installer.Length; i++) {
\r
290 Platform platform = InstalledPackage.Installer[i].Platform;
\r
293 if (platform != null) {
\r
294 pts = (platform.IsRunnable())? 10 : 0;
\r
295 pts += (platform.IsRunnableArchOnWow64() && platform.IsRunnableOS())? 1 : 0;
\r
296 } else { // if (platform == null) {
\r
297 pts = 1; // null の場合は動作プラットホーム扱い
\r
299 if (pts > bestVal) {
\r
309 /// インストーラの一時保存先パスを生成
\r
311 private string getArchiveFilePath()
\r
313 Package package = this.InstalledPackage;
\r
315 string folderName = string.Format("{0}({1})", package.Name, package.Version);
\r
316 string fileName = NaGet.Utils.Url2filename(package.Installer[0].Url.Href);
\r
318 string folder = Path.Combine(NaGet.Env.ArchiveFolderPath, folderName);
\r
320 if (! File.Exists(Path.Combine(folder, fileName))) {
\r
321 if (Directory.Exists(folder)) {
\r
322 if (! File.Exists(Path.Combine(folder, fileName))) {
\r
323 fileName = seekInstallerFile(folder, package.Type) ?? fileName;
\r
326 Directory.CreateDirectory(folder);
\r
330 return Path.Combine(folder, fileName);
\r
336 /// <param name="basedir">探すフォルダ</param>
\r
337 /// <param name="type">インストーラの種類</param>
\r
338 /// <returns>探し出されたインストーラファイルのフルパス。存在しないならばnull</returns>
\r
339 private static string seekInstallerFile(string basedir, InstallerType type)
\r
341 if (! Directory.Exists(basedir)) {
\r
345 System.Collections.Generic.List<string> list = new System.Collections.Generic.List<string>();
\r
347 case InstallerType.MSI_PACKAGE:
\r
348 list.AddRange(Directory.GetFiles(basedir, "*.msi"));
\r
350 case InstallerType.EXEC_INSTALLER:
\r
351 list.AddRange(Directory.GetFiles(basedir, "*.exe"));
\r
353 case InstallerType.ARCHIVE:
\r
354 list.AddRange(Directory.GetFiles(basedir, "*.zip"));
\r
355 list.AddRange(Directory.GetFiles(basedir, "*.lzh"));
\r
356 list.AddRange(Directory.GetFiles(basedir, "*.cab"));
\r
357 list.AddRange(Directory.GetFiles(basedir, "*.7z"));
\r
358 list.AddRange(Directory.GetFiles(basedir, "*.tar*"));
\r
366 delegate(string file) {
\r
367 return ! File.Exists(file);
\r
371 // "setup"の語が入ったファイルはインストーラとみなし、優先選択
\r
372 foreach (string path in list) {
\r
373 if (Path.GetFileName(path).ToLower().IndexOf("setup") >= 0) {
\r
379 return (list.Count > 0)? list[0] : null;
\r
382 public override string ToString()
\r
384 return string.Format("{0}({1})", InstalledPackage.Name, InstalledPackage.Version);
\r
387 public static string ToString(Installation[] installations)
\r
389 string[] strs = new string[installations.Length];
\r
390 for (int i = 0; i < installations.Length; i++) {
\r
391 strs[i] = installations[i].ToString();
\r
393 return string.Join(" ", strs);
\r
397 /// パッケージ配列をインストール処理配列に変換する便利メソッド
\r
399 /// <param name="pkgs">パッケージ配列</param>
\r
400 /// <returns>変換されたインストール処理配列</returns>
\r
401 public static Installation[] ConvertInstallations(Package[] pkgs)
\r
403 Installation[] insts = new Installation[pkgs.Length];
\r
404 for (int i = 0; i < pkgs.Length; i++) {
\r
405 insts[i] = new Installation(pkgs[i]);
\r