/// <inheritdoc/> public ExternalImplementation Lookup(ImplementationSelection selection) { #region Sanity checks if (selection == null) { throw new ArgumentNullException("selection"); } #endregion try { var referenceImpl = ExternalImplementation.FromID(selection.ID); // Reference implementation from ID does not contain all required information. // Therefore, find the original implementation. var implementation = GetImplementations(referenceImpl.Package) .First(x => x.Version == referenceImpl.Version && x.Architecture == referenceImpl.Architecture); CopyValues(from: selection, to: implementation); return(implementation); } #region Error handling catch (FormatException) { throw new ImplementationNotFoundException(string.Format(Resources.UnknownPackageID, selection.ID, DistributionName)); } catch (InvalidOperationException) { throw new ImplementationNotFoundException(string.Format(Resources.UnknownPackageID, selection.ID, DistributionName)); } #endregion }
/// <inheritdoc/> public ExternalImplementation?Lookup(ImplementationSelection selection) { #region Sanity checks if (selection == null) { throw new ArgumentNullException(nameof(selection)); } #endregion try { var referenceImpl = ExternalImplementation.FromID(selection.ID); // Reference implementation from ID does not contain all required information. // Therefore, find the original implementation. var implementation = GetImplementations(referenceImpl.Package) .FirstOrDefault(x => x.Version == referenceImpl.Version && x.Architecture == referenceImpl.Architecture); if (implementation != null) { CopyValues(from: selection, to: implementation); } return(implementation); } catch (FormatException) { return(null); } }
/// <summary> /// Applies an <see cref="ExecutableInVar"/> binding by creating a run-environment executable. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <param name="startInfo">The process launch environment to use to make the run-environment executable available.</param> /// <exception cref="KeyNotFoundException"><see cref="Selections"/> points to missing <see cref="Dependency"/>s.</exception> /// <exception cref="ImplementationNotFoundException">One of the <see cref="Store.Model.Implementation"/>s is not cached yet.</exception> /// <exception cref="ExecutorException"><see cref="ExecutableInVar.Name"/> is invalid.</exception> /// <exception cref="IOException">A problem occurred while writing the file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the file is not permitted.</exception> private void ApplyExecutableInVar(ExecutableInVar binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) { throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<executable-in-var>")); } if (Path.GetInvalidFileNameChars().Any(invalidChar => binding.Name.Contains(invalidChar.ToString(CultureInfo.InvariantCulture)))) { throw new ExecutorException(string.Format(Resources.IllegalCharInBindingName, @"<executable-in-var>")); } string exePath = DeployRunEnvExecutable(binding.Name); // Point variable directly to executable if (startInfo.EnvironmentVariables.ContainsKey(binding.Name)) { Log.Warn("Overwriting existing environment variable with <executable-in-var>: " + binding.Name); } startInfo.EnvironmentVariables[binding.Name] = exePath; // Tell the executable what command-line to run _runEnvPendings.Add(new RunEnvPending(binding.Name, GetCommandLine(implementation, binding.Command ?? Command.NameRun, startInfo))); }
/// <inheritdoc/> public ExternalImplementation Lookup(ImplementationSelection selection) { #region Sanity checks if (selection == null) throw new ArgumentNullException(nameof(selection)); #endregion try { var referenceImpl = ExternalImplementation.FromID(selection.ID); // Reference implementation from ID does not contain all required information. // Therefore, find the original implementation. var implementation = GetImplementations(referenceImpl.Package) .First(x => x.Version == referenceImpl.Version && x.Architecture == referenceImpl.Architecture); CopyValues(from: selection, to: implementation); return implementation; } #region Error handling catch (FormatException) { throw new ImplementationNotFoundException(string.Format(Resources.UnknownPackageID, selection.ID, DistributionName)); } catch (InvalidOperationException) { throw new ImplementationNotFoundException(string.Format(Resources.UnknownPackageID, selection.ID, DistributionName)); } #endregion }
/// <summary> /// Applies all <see cref="Binding"/>s listed in a specific <see cref="IBindingContainer"/>. /// </summary> /// <param name="bindingContainer">The list of <see cref="Binding"/>s to be performed.</param> /// <param name="implementation">The implementation to be made available via the <see cref="Binding"/>s.</param> /// <param name="startInfo">The process launch environment to apply the <see cref="Binding"/>s to.</param> /// <exception cref="ImplementationNotFoundException">The <paramref name="implementation"/> is not cached yet.</exception> /// <exception cref="ExecutorException">A <see cref="Command"/> contained invalid data.</exception> /// <exception cref="IOException">A problem occurred while writing a file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to a file is not permitted.</exception> private void ApplyBindings(IBindingContainer bindingContainer, ImplementationSelection implementation, ProcessStartInfo startInfo) { // Do not apply bindings more than once if (!_appliedBindingContainers.Add(bindingContainer)) { return; } if (bindingContainer.Bindings.Count == 0) { return; } // Don't use bindings for PackageImplementations if (implementation.ID.StartsWith(ExternalImplementation.PackagePrefix)) { return; } new PerTypeDispatcher <Binding>(ignoreMissing: true) { (EnvironmentBinding environmentBinding) => ApplyEnvironmentBinding(environmentBinding, implementation, startInfo), //(OverlayBinding overlayBinding) => ApplyOverlayBinding(overlayBinding, implementation, startInfo), (ExecutableInVar executableInVar) => ApplyExecutableInVar(executableInVar, implementation, startInfo), (ExecutableInPath executableInPath) => ApplyExecutableInPath(executableInPath, implementation, startInfo) }.Dispatch(bindingContainer.Bindings); }
/// <summary> /// Applies an <see cref="EnvironmentBinding"/> by modifying environment variables. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <param name="startInfo">The process launch environment to apply the binding to.</param> /// <exception cref="ExecutorException"><see cref="EnvironmentBinding.Name"/> or other data is invalid.</exception> private void ApplyEnvironmentBinding(EnvironmentBinding binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) { throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<environment>")); } var environmentVariables = startInfo.EnvironmentVariables; string newValue; if (binding.Value != null && binding.Insert != null) { // Conflict throw new ExecutorException(Resources.EnvironmentBindingValueInvalid); } else if (binding.Value != null) { // Static value newValue = binding.Value; } else { // Path inside the implementation newValue = Path.Combine(GetImplementationPath(implementation), FileUtils.UnifySlashes(binding.Insert ?? "")); } // Set the default value if the variable is not already set on the system if (!environmentVariables.ContainsKey(binding.Name)) { environmentVariables.Add(binding.Name, binding.Default); } string previousValue = environmentVariables[binding.Name]; string separator = (string.IsNullOrEmpty(binding.Separator) ? Path.PathSeparator.ToString(CultureInfo.InvariantCulture) : binding.Separator); switch (binding.Mode) { default: case EnvironmentMode.Prepend: environmentVariables[binding.Name] = string.IsNullOrEmpty(previousValue) // No exisiting value, just set new one ? newValue // Prepend new value to existing one seperated by path separator : newValue + separator + environmentVariables[binding.Name]; break; case EnvironmentMode.Append: environmentVariables[binding.Name] = string.IsNullOrEmpty(previousValue) // No exisiting value, just set new one ? newValue // Append new value to existing one seperated by path separator : environmentVariables[binding.Name] + separator + newValue; break; case EnvironmentMode.Replace: // Overwrite any existing value environmentVariables[binding.Name] = newValue; break; } }
private List <ArgBase> GetCommandLine([NotNull] ImplementationSelection implementation, [NotNull] string commandName, [NotNull] ProcessStartInfo startInfo) { #region Sanity checks if (implementation == null) { throw new ArgumentNullException(nameof(implementation)); } if (startInfo == null) { throw new ArgumentNullException(nameof(startInfo)); } if (commandName == null) { throw new ArgumentNullException(nameof(commandName)); } #endregion if (commandName.Length == 0) { throw new ExecutorException(string.Format(Resources.CommandNotSpecified, implementation.InterfaceUri)); } var command = implementation[commandName]; Debug.Assert(command != null); // Apply bindings implementations use to find themselves and their dependencies ApplyBindings(command, implementation, startInfo); if (command.WorkingDir != null) { ApplyWorkingDir(command.WorkingDir, implementation, startInfo); } ApplyDependencyBindings(command, startInfo); List <ArgBase> commandLine; var runner = command.Runner; if (runner == null) { commandLine = new List <ArgBase>(); } else { commandLine = GetCommandLine(Selections[runner.InterfaceUri], runner.Command ?? Command.NameRun, startInfo); commandLine.AddRange(runner.Arguments); } if (!string.IsNullOrEmpty(command.Path)) { string path = FileUtils.UnifySlashes(command.Path); // Fully qualified paths are used by package/native implementations, usually relative to the implementation commandLine.Add(Path.IsPathRooted(path) ? path : Path.Combine(GetImplementationPath(implementation), path)); } commandLine.AddRange(command.Arguments); return(commandLine); }
/// <summary> /// Retrieves the original <see cref="Implementation"/> an <see cref="ImplementationSelection"/> was based ofF. /// </summary> public Implementation LookupOriginalImplementation(ImplementationSelection implemenationSelection) { #region Sanity checks if (implemenationSelection == null) { throw new ArgumentNullException(nameof(implemenationSelection)); } #endregion return(implemenationSelection.ID.StartsWith(ExternalImplementation.PackagePrefix) ? _externalImplementations[implemenationSelection.ID] : _feedManager[implemenationSelection.FromFeed ?? implemenationSelection.InterfaceUri][implemenationSelection.ID]); }
/// <summary> /// Turns a <see cref="SelectionCandidate"/> into a <see cref="ImplementationSelection"/>. /// </summary> /// <param name="candidate">The selection candidate.</param> /// <param name="requirements">The requirements the candidate was chosen for.</param> /// <param name="allCandidates">All candidates that were considered for selection (including <paramref name="candidate"/>). These are used to present the user with possible alternatives.</param> public static ImplementationSelection ToSelection(this SelectionCandidate candidate, Requirements requirements, [InstantHandle] IEnumerable <SelectionCandidate> allCandidates) { #region Sanity checks if (candidate == null) { throw new ArgumentNullException(nameof(candidate)); } if (allCandidates == null) { throw new ArgumentNullException(nameof(allCandidates)); } if (requirements == null) { throw new ArgumentNullException(nameof(requirements)); } #endregion var implementation = candidate.Implementation; var selection = new ImplementationSelection(allCandidates) { ID = implementation.ID, LocalPath = implementation.LocalPath, ManifestDigest = implementation.ManifestDigest, Architecture = implementation.Architecture, Version = implementation.Version, Released = implementation.Released, Stability = candidate.EffectiveStability, License = implementation.License, UnknownAttributes = implementation.UnknownAttributes, UnknownElements = implementation.UnknownElements, InterfaceUri = requirements.InterfaceUri }; if (candidate.FeedUri != requirements.InterfaceUri) { selection.FromFeed = candidate.FeedUri; } if (implementation is ExternalImplementation externalImplementation) { selection.QuickTestFile = externalImplementation.QuickTestFile; } selection.Bindings.AddRange(implementation.Bindings.CloneElements()); selection.AddDependencies(requirements, from: candidate.Implementation); selection.AddCommand(requirements, from: candidate.Implementation); return(selection); }
/// <summary> /// Applies all <see cref="Binding"/>s listed in a specific <see cref="IBindingContainer"/>. /// </summary> /// <param name="bindingContainer">The list of <see cref="Binding"/>s to be performed.</param> /// <param name="implementation">The implementation to be made available via the <see cref="Binding"/>s.</param> /// <param name="startInfo">The process launch environment to apply the <see cref="Binding"/>s to.</param> /// <exception cref="ImplementationNotFoundException">The <paramref name="implementation"/> is not cached yet.</exception> /// <exception cref="ExecutorException">A <see cref="Command"/> contained invalid data.</exception> /// <exception cref="IOException">A problem occurred while writing a file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to a file is not permitted.</exception> private void ApplyBindings(IBindingContainer bindingContainer, ImplementationSelection implementation, ProcessStartInfo startInfo) { // Do not apply bindings more than once if (!_appliedBindingContainers.Add(bindingContainer)) return; if (bindingContainer.Bindings.Count == 0) return; // Don't use bindings for PackageImplementations if (implementation.ID.StartsWith(ExternalImplementation.PackagePrefix)) return; new PerTypeDispatcher<Binding>(ignoreMissing: true) { (EnvironmentBinding environmentBinding) => ApplyEnvironmentBinding(environmentBinding, implementation, startInfo), //(OverlayBinding overlayBinding) => ApplyOverlayBinding(overlayBinding, implementation, startInfo), (ExecutableInVar executableInVar) => ApplyExecutableInVar(executableInVar, implementation, startInfo), (ExecutableInPath executableInPath) => ApplyExecutableInPath(executableInPath, implementation, startInfo) }.Dispatch(bindingContainer.Bindings); }
/// <summary> /// Applies an <see cref="EnvironmentBinding"/> by modifying environment variables. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <exception cref="ExecutorException"><see cref="EnvironmentBinding.Name"/> or other data is invalid.</exception> private void ApplyEnvironmentBinding(EnvironmentBinding binding, ImplementationSelection implementation) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) { throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<environment>")); } var environmentVariables = _startInfo.EnvironmentVariables; string newValue; if (binding.Value != null && binding.Insert != null) { // Conflict throw new ExecutorException(Resources.EnvironmentBindingValueInvalid); } else if (binding.Value != null) { // Static value newValue = binding.Value; } else { // Path inside the implementation newValue = Path.Combine(_implementationStore.GetPath(implementation), FileUtils.UnifySlashes(binding.Insert ?? "")); } // Set the default value if the variable is not already set on the system if (!environmentVariables.ContainsKey(binding.Name)) { environmentVariables.Add(binding.Name, binding.Default); } string previousValue = environmentVariables[binding.Name]; string separator = (string.IsNullOrEmpty(binding.Separator) ? Path.PathSeparator.ToString(CultureInfo.InvariantCulture) : binding.Separator); environmentVariables[binding.Name] = binding.Mode switch { _ when string.IsNullOrEmpty(previousValue) => newValue, EnvironmentMode.Replace => newValue, EnvironmentMode.Prepend => newValue + separator + environmentVariables[binding.Name], EnvironmentMode.Append => environmentVariables[binding.Name] + separator + newValue, _ => throw new InvalidOperationException($"Unknown {nameof(EnvironmentBinding)} value: {binding.Mode}") }; }
/// <summary> /// Applies a <see cref="WorkingDir"/> change to the <see cref="ProcessStartInfo"/>. /// </summary> /// <param name="binding">The <see cref="WorkingDir"/> to apply.</param> /// <param name="implementation">The implementation to be made available via the <see cref="WorkingDir"/> change.</param> /// <param name="startInfo">The process launch environment to apply the <see cref="WorkingDir"/> change to.</param> /// <exception cref="ImplementationNotFoundException">The <paramref name="implementation"/> is not cached yet.</exception> /// <exception cref="ExecutorException">The <paramref name="binding"/> has an invalid path or another working directory has already been set.</exception> /// <remarks>This method can only be called successfully once per <see cref="BuildStartInfoWithBindings()"/>.</remarks> private void ApplyWorkingDir(WorkingDir binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); string source = FileUtils.UnifySlashes(binding.Source) ?? ""; if (Path.IsPathRooted(source) || source.Contains(".." + Path.DirectorySeparatorChar)) { throw new ExecutorException(Resources.WorkingDirInvalidPath); } // Only allow working directory to be changed once if (!string.IsNullOrEmpty(startInfo.WorkingDirectory)) { throw new ExecutorException(Resources.Working); } startInfo.WorkingDirectory = Path.Combine(GetImplementationPath(implementation), source); }
public static Command?AddCommand(this ImplementationSelection selection, Requirements requirements, Implementation from) { #region Sanity checks if (selection == null) { throw new ArgumentNullException(nameof(selection)); } if (requirements == null) { throw new ArgumentNullException(nameof(requirements)); } if (from == null) { throw new ArgumentNullException(nameof(from)); } #endregion Debug.Assert(requirements.Command != null); var command = from[requirements.Command]; if (command == null) { return(null); } var newCommand = new Command { Name = command.Name, Path = command.Path }; newCommand.Arguments.AddRange(command.Arguments.CloneElements()); newCommand.Bindings.AddRange(command.Bindings.CloneElements()); if (command.WorkingDir != null) { newCommand.WorkingDir = command.WorkingDir.Clone(); } if (command.Runner != null) { newCommand.Runner = command.Runner.CloneRunner(); } newCommand.AddDependencies(requirements, from: command); selection.Commands.Add(newCommand); return(newCommand); }
/// <summary> /// Applies an <see cref="ExecutableInPath"/> binding by creating a run-environment executable. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <param name="startInfo">The process launch environment to use to make the run-environment executable available.</param> /// <exception cref="KeyNotFoundException"><see cref="Selections"/> points to missing <see cref="Dependency"/>s.</exception> /// <exception cref="ImplementationNotFoundException">One of the <see cref="Store.Model.Implementation"/>s is not cached yet.</exception> /// <exception cref="ExecutorException"><see cref="ExecutableInPath.Name"/> is invalid.</exception> /// <exception cref="IOException">A problem occurred while writing the file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the file is not permitted.</exception> private void ApplyExecutableInPath(ExecutableInPath binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) { throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<executable-in-path>")); } if (Path.GetInvalidFileNameChars().Any(invalidChar => binding.Name.Contains(invalidChar.ToString(CultureInfo.InvariantCulture)))) { throw new ExecutorException(string.Format(Resources.IllegalCharInBindingName, @"<executable-in-path>")); } string exePath = DeployRunEnvExecutable(binding.Name); // Add executable directory to PATH variable startInfo.EnvironmentVariables["PATH"] = Path.GetDirectoryName(exePath) + Path.PathSeparator + startInfo.EnvironmentVariables["PATH"]; // Tell the executable what command-line to run _runEnvPendings.Add(new RunEnvPending(binding.Name, GetCommandLine(implementation, binding.Command ?? Command.NameRun, startInfo))); }
/// <summary> /// Generates <see cref="SolverDemand"/>s for the dependencies specified by an <see cref="ImplementationSelection"/>. /// </summary> /// <param name="selection">The selection to scan for dependencies.</param> /// <param name="requirements">Requirements to inherit into the demands.</param> protected IEnumerable <SolverDemand> DemandsFor(ImplementationSelection selection, Requirements requirements) { foreach (var demand in selection.Dependencies.SelectMany(DemandsFor)) { yield return(demand); } foreach (var demands in DemandsFor(selection.Bindings, selection.InterfaceUri)) { yield return(demands); } var command = selection[requirements.Command ?? Command.NameRun]; if (command != null) { foreach (var demand in DemandsFor(command, requirements.InterfaceUri)) { yield return(demand); } } }
/// <summary> /// Applies all <see cref="Binding"/>s listed in a specific <see cref="IBindingContainer"/>. /// </summary> /// <param name="bindingContainer">The list of <see cref="Binding"/>s to be performed.</param> /// <param name="implementation">The implementation to be made available via the <see cref="Binding"/>s.</param> /// <exception cref="ImplementationNotFoundException">The <paramref name="implementation"/> is not cached yet.</exception> /// <exception cref="ExecutorException">A <see cref="Command"/> contained invalid data.</exception> /// <exception cref="IOException">A problem occurred while writing a file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to a file is not permitted.</exception> private void ApplyBindings(IBindingContainer bindingContainer, ImplementationSelection implementation) { // Do not apply bindings more than once if (!_appliedBindingContainers.Add(bindingContainer)) { return; } if (bindingContainer.Bindings.Count == 0) { return; } // Don't use bindings for PackageImplementations if (implementation.ID.StartsWith(ExternalImplementation.PackagePrefix)) { return; } foreach (var binding in bindingContainer.Bindings) { switch (binding) { case EnvironmentBinding environmentBinding: ApplyEnvironmentBinding(environmentBinding, implementation); break; //case OverlayBinding overlayBinding: ApplyOverlayBinding(overlayBinding, implementation); break; case ExecutableInVar executableInVar: ApplyExecutableInVar(executableInVar, implementation); break; case ExecutableInPath executableInPath: ApplyExecutableInPath(executableInPath, implementation); break; } } }
/// <inheritdoc/> public string GetImplementationPath(ImplementationSelection implementation) { #region Sanity checks if (implementation == null) { throw new ArgumentNullException(nameof(implementation)); } #endregion if (string.IsNullOrEmpty(implementation.LocalPath)) { string path = _store.GetPath(implementation.ManifestDigest); if (path == null) { throw new ImplementationNotFoundException(implementation.ManifestDigest); } return(path); } else { return(implementation.LocalPath); } }
/// <summary> /// Always throws <see cref="ImplementationNotFoundException"/>. /// </summary> public ExternalImplementation Lookup(ImplementationSelection selection) { throw new ImplementationNotFoundException(Resources.NoPackageManagerSupport); }
/// <inheritdoc/> public ExternalImplementation?Lookup(ImplementationSelection selection) => _packageManagers .Select(x => x.Lookup(selection)) .WhereNotNull() .FirstOrDefault();
private bool TryToUseExistingCandidate([NotNull] Requirements requirements, [NotNull, ItemNotNull] IEnumerable <SelectionCandidate> suitableCandidates, [NotNull] ImplementationSelection selection) { if (!suitableCandidates.Contains(selection)) { return(false); } if (selection.ContainsCommand(requirements.Command)) { return(true); } var command = selection.AddCommand(requirements, from: _candidateProvider.LookupOriginalImplementation(selection)); return(TryToSolveCommand(command, requirements)); }
/// <summary> /// Applies an <see cref="ExecutableInVar"/> binding by creating a run-environment executable. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <param name="startInfo">The process launch environment to use to make the run-environment executable available.</param> /// <exception cref="KeyNotFoundException"><see cref="Selections"/> points to missing <see cref="Dependency"/>s.</exception> /// <exception cref="ImplementationNotFoundException">One of the <see cref="Store.Model.Implementation"/>s is not cached yet.</exception> /// <exception cref="ExecutorException"><see cref="ExecutableInVar.Name"/> is invalid.</exception> /// <exception cref="IOException">A problem occurred while writing the file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the file is not permitted.</exception> private void ApplyExecutableInVar(ExecutableInVar binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<executable-in-var>")); if (Path.GetInvalidFileNameChars().Any(invalidChar => binding.Name.Contains(invalidChar.ToString(CultureInfo.InvariantCulture)))) throw new ExecutorException(string.Format(Resources.IllegalCharInBindingName, @"<executable-in-var>")); string exePath = DeployRunEnvExecutable(binding.Name); // Point variable directly to executable if (startInfo.EnvironmentVariables.ContainsKey(binding.Name)) Log.Warn("Overwriting existing environment variable with <executable-in-var>: " + binding.Name); startInfo.EnvironmentVariables[binding.Name] = exePath; // Tell the executable what command-line to run _runEnvPendings.Add(new RunEnvPending(binding.Name, GetCommandLine(implementation, binding.Command ?? Command.NameRun, startInfo))); }
public static bool Contains([NotNull] this IEnumerable <SelectionCandidate> candidates, [NotNull] ImplementationSelection implementation) { #region Sanity checks if (candidates == null) { throw new ArgumentNullException("candidates"); } if (implementation == null) { throw new ArgumentNullException("implementation"); } #endregion return(candidates.Select(x => x.Implementation.ID).Contains(implementation.ID)); }
/// <summary> /// Applies an <see cref="EnvironmentBinding"/> by modifying environment variables. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <param name="startInfo">The process launch environment to apply the binding to.</param> /// <exception cref="ExecutorException"><see cref="EnvironmentBinding.Name"/> or other data is invalid.</exception> private void ApplyEnvironmentBinding(EnvironmentBinding binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<environment>")); var environmentVariables = startInfo.EnvironmentVariables; string newValue; if (binding.Value != null && binding.Insert != null) { // Conflict throw new ExecutorException(Resources.EnvironmentBindingValueInvalid); } else if (binding.Value != null) { // Static value newValue = binding.Value; } else { // Path inside the implementation newValue = Path.Combine(GetImplementationPath(implementation), FileUtils.UnifySlashes(binding.Insert ?? "")); } // Set the default value if the variable is not already set on the system if (!environmentVariables.ContainsKey(binding.Name)) environmentVariables.Add(binding.Name, binding.Default); string previousValue = environmentVariables[binding.Name]; string separator = (string.IsNullOrEmpty(binding.Separator) ? Path.PathSeparator.ToString(CultureInfo.InvariantCulture) : binding.Separator); switch (binding.Mode) { default: case EnvironmentMode.Prepend: environmentVariables[binding.Name] = string.IsNullOrEmpty(previousValue) // No exisiting value, just set new one ? newValue // Prepend new value to existing one seperated by path separator : newValue + separator + environmentVariables[binding.Name]; break; case EnvironmentMode.Append: environmentVariables[binding.Name] = string.IsNullOrEmpty(previousValue) // No exisiting value, just set new one ? newValue // Append new value to existing one seperated by path separator : environmentVariables[binding.Name] + separator + newValue; break; case EnvironmentMode.Replace: // Overwrite any existing value environmentVariables[binding.Name] = newValue; break; } }
/// <summary> /// Applies an <see cref="ExecutableInPath"/> binding by creating a run-environment executable. /// </summary> /// <param name="binding">The binding to apply.</param> /// <param name="implementation">The implementation to be made available.</param> /// <param name="startInfo">The process launch environment to use to make the run-environment executable available.</param> /// <exception cref="KeyNotFoundException"><see cref="Selections"/> points to missing <see cref="Dependency"/>s.</exception> /// <exception cref="ImplementationNotFoundException">One of the <see cref="Store.Model.Implementation"/>s is not cached yet.</exception> /// <exception cref="ExecutorException"><see cref="ExecutableInPath.Name"/> is invalid.</exception> /// <exception cref="IOException">A problem occurred while writing the file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the file is not permitted.</exception> private void ApplyExecutableInPath(ExecutableInPath binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); if (string.IsNullOrEmpty(binding.Name)) throw new ExecutorException(string.Format(Resources.MissingBindingName, @"<executable-in-path>")); if (Path.GetInvalidFileNameChars().Any(invalidChar => binding.Name.Contains(invalidChar.ToString(CultureInfo.InvariantCulture)))) throw new ExecutorException(string.Format(Resources.IllegalCharInBindingName, @"<executable-in-path>")); string exePath = DeployRunEnvExecutable(binding.Name); // Add executable directory to PATH variable startInfo.EnvironmentVariables["PATH"] = Path.GetDirectoryName(exePath) + Path.PathSeparator + startInfo.EnvironmentVariables["PATH"]; // Tell the executable what command-line to run _runEnvPendings.Add(new RunEnvPending(binding.Name, GetCommandLine(implementation, binding.Command ?? Command.NameRun, startInfo))); }
/// <summary> /// Determines the command-line needed to execute an <see cref="ImplementationSelection"/>. Recursivley handles <see cref="Runner"/>s. /// </summary> /// <param name="implementation">The implementation to launch.</param> /// <param name="commandName">The name of the <see cref="Command"/> within the <paramref name="implementation"/> to launch.</param> /// <param name="startInfo">The process launch environment to apply additional <see cref="Binding"/> to.</param> /// <exception cref="KeyNotFoundException"><see cref="Selections"/> points to missing <see cref="Dependency"/>s.</exception> /// <exception cref="ImplementationNotFoundException">An <see cref="Implementation"/> is not cached yet.</exception> /// <exception cref="ExecutorException">A <see cref="Command"/> contained invalid data.</exception> /// <exception cref="IOException">A problem occurred while writing a file.</exception> /// <exception cref="UnauthorizedAccessException">Write access to a file is not permitted.</exception> private List<ArgBase> GetCommandLine(ImplementationSelection implementation, string commandName, ProcessStartInfo startInfo) { #region Sanity checks if (implementation == null) throw new ArgumentNullException("implementation"); if (startInfo == null) throw new ArgumentNullException("startInfo"); #endregion if (string.IsNullOrEmpty(commandName)) throw new ExecutorException(string.Format(Resources.CommandNotSpecified, implementation.InterfaceUri)); Command command = implementation[commandName]; // Apply bindings implementations use to find themselves and their dependencies ApplyBindings(command, implementation, startInfo); if (command.WorkingDir != null) ApplyWorkingDir(command.WorkingDir, implementation, startInfo); ApplyDependencyBindings(command, startInfo); List<ArgBase> commandLine; var runner = command.Runner; if (runner == null) commandLine = new List<ArgBase>(); else { commandLine = GetCommandLine(Selections[runner.InterfaceUri], runner.Command ?? Command.NameRun, startInfo); commandLine.AddRange(runner.Arguments); } if (!string.IsNullOrEmpty(command.Path)) { string path = FileUtils.UnifySlashes(command.Path); // Fully qualified paths are used by package/native implementations, usually relative to the implementation commandLine.Add(Path.IsPathRooted(path) ? path : Path.Combine(GetImplementationPath(implementation), path)); } commandLine.AddRange(command.Arguments); return commandLine; }
public static ImplementationSelection ToSelection([NotNull] this SelectionCandidate candidate, [NotNull, ItemNotNull] IEnumerable<SelectionCandidate> allCandidates, [NotNull] Requirements requirements) { #region Sanity checks if (candidate == null) throw new ArgumentNullException("candidate"); if (allCandidates == null) throw new ArgumentNullException("allCandidates"); if (requirements == null) throw new ArgumentNullException("requirements"); #endregion var implementation = candidate.Implementation; var selection = new ImplementationSelection(allCandidates) { ID = implementation.ID, LocalPath = implementation.LocalPath, ManifestDigest = implementation.ManifestDigest, Architecture = implementation.Architecture, Version = implementation.Version, Released = implementation.Released, Stability = candidate.EffectiveStability, License = implementation.License, InterfaceUri = requirements.InterfaceUri }; if (candidate.FeedUri != requirements.InterfaceUri) selection.FromFeed = candidate.FeedUri; var externalImplementation = implementation as ExternalImplementation; if (externalImplementation != null) selection.QuickTestFile = externalImplementation.QuickTestFile; selection.Bindings.AddRange(implementation.Bindings.CloneElements()); selection.AddDependencies(requirements, from: candidate.Implementation); selection.AddCommand(requirements, from: candidate.Implementation); return selection; }
private static string GetPath([NotNull] this ImplementationSelection implementation, [NotNull] IStore store) { return(implementation.ID.StartsWith(ExternalImplementation.PackagePrefix) ? "(" + implementation.ID + ")" : store.GetPath(implementation.ManifestDigest)); }
/// <summary> /// Retrieves the original <see cref="Implementation"/> an <see cref="ImplementationSelection"/> was based ofF. /// </summary> public Implementation LookupOriginalImplementation(ImplementationSelection implemenationSelection) { #region Sanity checks if (implemenationSelection == null) throw new ArgumentNullException("implemenationSelection"); #endregion return implemenationSelection.ID.StartsWith(ExternalImplementation.PackagePrefix) ? _externalImplementations[implemenationSelection.ID] : _feeds[implemenationSelection.FromFeed ?? implemenationSelection.InterfaceUri][implemenationSelection.ID]; }
/// <inheritdoc/> public string GetImplementationPath(ImplementationSelection implementation) { #region Sanity checks if (implementation == null) throw new ArgumentNullException(nameof(implementation)); #endregion if (string.IsNullOrEmpty(implementation.LocalPath)) { string path = _store.GetPath(implementation.ManifestDigest); if (path == null) throw new ImplementationNotFoundException(implementation.ManifestDigest); return path; } else return implementation.LocalPath; }
/// <summary> /// Applies a <see cref="WorkingDir"/> change to the <see cref="ProcessStartInfo"/>. /// </summary> /// <param name="binding">The <see cref="WorkingDir"/> to apply.</param> /// <param name="implementation">The implementation to be made available via the <see cref="WorkingDir"/> change.</param> /// <param name="startInfo">The process launch environment to apply the <see cref="WorkingDir"/> change to.</param> /// <exception cref="ImplementationNotFoundException">The <paramref name="implementation"/> is not cached yet.</exception> /// <exception cref="ExecutorException">The <paramref name="binding"/> has an invalid path or another working directory has already been set.</exception> /// <remarks>This method can only be called successfully once per <see cref="BuildStartInfoWithBindings()"/>.</remarks> private void ApplyWorkingDir(WorkingDir binding, ImplementationSelection implementation, ProcessStartInfo startInfo) { Log.Debug("Applying " + binding + " for " + implementation); string source = FileUtils.UnifySlashes(binding.Source) ?? ""; if (Path.IsPathRooted(source) || source.Contains(".." + Path.DirectorySeparatorChar)) throw new ExecutorException(Resources.WorkingDirInvalidPath); // Only allow working directory to be changed once if (!string.IsNullOrEmpty(startInfo.WorkingDirectory)) throw new ExecutorException(Resources.Working); startInfo.WorkingDirectory = Path.Combine(GetImplementationPath(implementation), source); }