RDI 0.1.0.20091020
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +3 -0
- data/ChangeLog +46 -0
- data/Credits +21 -0
- data/LICENSE +31 -0
- data/README +18 -0
- data/TODO +17 -0
- data/lib/rdi/Installer.rb +678 -0
- data/lib/rdi/Model/ContextModifier.rb +20 -0
- data/lib/rdi/Model/DependencyDescription.rb +154 -0
- data/lib/rdi/Model/DependencyUserChoice.rb +202 -0
- data/lib/rdi/Model/Installer.rb +24 -0
- data/lib/rdi/Model/LocationSelector.rb +16 -0
- data/lib/rdi/Model/ProgressView.rb +16 -0
- data/lib/rdi/Model/Tester.rb +20 -0
- data/lib/rdi/Model/View.rb +16 -0
- data/lib/rdi/Plugins/ContextModifiers/GemPath.desc.rb +13 -0
- data/lib/rdi/Plugins/ContextModifiers/GemPath.rb +106 -0
- data/lib/rdi/Plugins/ContextModifiers/LibraryPath.rb +74 -0
- data/lib/rdi/Plugins/ContextModifiers/RubyLoadPath.rb +76 -0
- data/lib/rdi/Plugins/ContextModifiers/SystemPath.rb +74 -0
- data/lib/rdi/Plugins/GemCommon.rb +127 -0
- data/lib/rdi/Plugins/Installers/Download.rb +72 -0
- data/lib/rdi/Plugins/Installers/DownloadAndInstall.desc.rb +11 -0
- data/lib/rdi/Plugins/Installers/DownloadAndInstall.rb +54 -0
- data/lib/rdi/Plugins/Installers/Gem.desc.rb +13 -0
- data/lib/rdi/Plugins/Installers/Gem.rb +67 -0
- data/lib/rdi/Plugins/Installers/Icons/Download.png +0 -0
- data/lib/rdi/Plugins/Installers/Icons/DownloadAndInstall.png +0 -0
- data/lib/rdi/Plugins/Installers/Icons/Gem.png +0 -0
- data/lib/rdi/Plugins/Installers/Yum.rb +52 -0
- data/lib/rdi/Plugins/ProgressViews/SimpleWxGUI.desc.rb +13 -0
- data/lib/rdi/Plugins/ProgressViews/SimpleWxGUI.rb +58 -0
- data/lib/rdi/Plugins/ProgressViews/Text.rb +68 -0
- data/lib/rdi/Plugins/RubyGemsDepDesc.rb +41 -0
- data/lib/rdi/Plugins/Testers/Binaries.rb +56 -0
- data/lib/rdi/Plugins/Testers/DynamicLibraries.rb +56 -0
- data/lib/rdi/Plugins/Testers/RubyRequires.rb +76 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/DependenciesLoaderDialog.rb +147 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/DependencyPanel.rb +301 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/Icons/Dependency.png +0 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/Icons/Ignore.png +0 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/Icons/ValidKO.png +0 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/Icons/ValidOK.png +0 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/LocationSelectors/Directory.desc.rb +13 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI/LocationSelectors/Directory.rb +45 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI.desc.rb +13 -0
- data/lib/rdi/Plugins/Views/SimpleWxGUI.rb +60 -0
- data/lib/rdi/Plugins/Views/Text/LocationSelectors/Directory.rb +44 -0
- data/lib/rdi/Plugins/Views/Text.rb +257 -0
- data/lib/rdi/Plugins/WxCommon.rb +69 -0
- data/lib/rdi/Plugins/WxRubyDepDesc.rb +36 -0
- data/lib/rdi/rdi.rb +8 -0
- data/test/Common.rb +796 -0
- data/test/Flows/BasicFlows.rb +114 -0
- data/test/Flows/UIFlows.rb +460 -0
- data/test/Plugins/ContextModifiers/GemPath.rb +44 -0
- data/test/Plugins/ContextModifiers/LibraryPath.rb +36 -0
- data/test/Plugins/ContextModifiers/RubyLoadPath.rb +36 -0
- data/test/Plugins/ContextModifiers/SystemPath.rb +36 -0
- data/test/Plugins/Installers/Download.rb +65 -0
- data/test/Plugins/Installers/DownloadAndInstall.rb +73 -0
- data/test/Plugins/Installers/Gem.rb +79 -0
- data/test/Plugins/LocationSelectors/Directory.rb +34 -0
- data/test/Plugins/Testers/Binaries.rb +46 -0
- data/test/Plugins/Testers/DynamicLibraries.rb +46 -0
- data/test/Plugins/Testers/RubyRequires.rb +48 -0
- data/test/Plugins/Views/SimpleWxGUI.rb +190 -0
- data/test/Plugins/Views/Text.rb +112 -0
- data/test/Plugins/WxEnv.rb +58 -0
- data/test/Plugins/WxEnvApp.rb +53 -0
- data/test/Repository/Binaries/DummyBinary +1 -0
- data/test/Repository/Libraries/DummyLibrary.so +1 -0
- data/test/Repository/RubyGems/DummyGem-0.0.1.20090828.gem +0 -0
- data/test/Repository/RubyGems/GemsSources/DummyGem/DummyGem.gemspec.rb +23 -0
- data/test/Repository/RubyGems/GemsSources/DummyGem/lib/DummyGemMain.rb +5 -0
- data/test/Repository/RubyLibraries/DummyRubyLib.rb +1 -0
- data/test/run.rb +23 -0
- metadata +145 -0
@@ -0,0 +1,678 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module RDI
|
7
|
+
|
8
|
+
# Constants reflecting the different installation destinations
|
9
|
+
DEST_LOCAL = 0
|
10
|
+
DEST_SYSTEM = 1
|
11
|
+
DEST_USER = 2
|
12
|
+
DEST_TEMP = 3
|
13
|
+
DEST_OTHER = 4
|
14
|
+
|
15
|
+
# The main class that performs operations
|
16
|
+
class Installer
|
17
|
+
|
18
|
+
# == Public API ==
|
19
|
+
|
20
|
+
# The local ext dir (platform specific, local to the application)
|
21
|
+
# String
|
22
|
+
attr_reader :ExtDir
|
23
|
+
|
24
|
+
# The system dir
|
25
|
+
# String
|
26
|
+
attr_reader :SystemDir
|
27
|
+
|
28
|
+
# The user dir
|
29
|
+
# String
|
30
|
+
attr_reader :UserDir
|
31
|
+
|
32
|
+
# The temporary dir
|
33
|
+
# String
|
34
|
+
attr_reader :TempDir
|
35
|
+
|
36
|
+
# The RDI lib dir (useful to parse for files among plugins)
|
37
|
+
# String
|
38
|
+
attr_reader :RDILibDir
|
39
|
+
|
40
|
+
# Constructor
|
41
|
+
#
|
42
|
+
# Parameters:
|
43
|
+
# * *iAppRootDir* (_String_): Application's root directory
|
44
|
+
# * *iMainInstance* (_Boolean_): Is this instance supposed to be the main one if none was defined before ? [optional = true]
|
45
|
+
def initialize(iAppRootDir, iMainInstance = true)
|
46
|
+
if ((iMainInstance) and
|
47
|
+
(@@MainInstallerInstance == nil))
|
48
|
+
@@MainInstallerInstance = self
|
49
|
+
end
|
50
|
+
@DefaultOptions = {}
|
51
|
+
|
52
|
+
# Set directories
|
53
|
+
@RDILibDir = File.dirname(__FILE__)
|
54
|
+
@ExtDir = "#{iAppRootDir}/ext/#{RUBY_PLATFORM}"
|
55
|
+
# Initialize all other standard directories
|
56
|
+
case RUBY_PLATFORM
|
57
|
+
when 'i386-mswin32'
|
58
|
+
@UserRootDir = "#{ENV['USERPROFILE']}/RDI"
|
59
|
+
@SystemDir = "#{ENV['SystemRoot']}/RDI"
|
60
|
+
when 'i386-linux'
|
61
|
+
@UserRootDir = "#{File.expand_path('~')}/RDI"
|
62
|
+
@SystemDir = "/usr/local/RDI"
|
63
|
+
else
|
64
|
+
logBug "RDI is not yet compatible with #{RUBY_PLATFORM}. Sorry. Please open a request on http://sourceforge.net/tracker/?group_id=274498&atid=1166451"
|
65
|
+
raise RuntimeError, "Incompatible platform: #{RUBY_PLATFORM}"
|
66
|
+
end
|
67
|
+
require 'tmpdir'
|
68
|
+
@TempRootDir = "#{Dir.tmpdir}/RDI"
|
69
|
+
@UserDir = "#{@UserRootDir}/#{RUBY_PLATFORM}"
|
70
|
+
@TempDir = "#{@TempRootDir}/#{RUBY_PLATFORM}"
|
71
|
+
require 'fileutils'
|
72
|
+
FileUtils::mkdir_p(@TempDir)
|
73
|
+
|
74
|
+
# Get all possible plugins
|
75
|
+
if (defined?(RUtilAnts::Plugins::PluginsManager) == nil)
|
76
|
+
require 'rUtilAnts/Plugins'
|
77
|
+
end
|
78
|
+
@Plugins = RUtilAnts::Plugins::PluginsManager.new
|
79
|
+
# Get the RDI root directory for libraries
|
80
|
+
# Get all plugins
|
81
|
+
@Plugins.parsePluginsFromDir('ContextModifiers', "#{@RDILibDir}/Plugins/ContextModifiers", 'RDI::ContextModifiers') do |ioPlugin|
|
82
|
+
# Cache some attributes
|
83
|
+
ioPlugin.LocationSelectorName = ioPlugin.getLocationSelectorName
|
84
|
+
end
|
85
|
+
@Plugins.parsePluginsFromDir('Installers', "#{@RDILibDir}/Plugins/Installers", 'RDI::Installers') do |ioPlugin|
|
86
|
+
ioPlugin.Installer = self
|
87
|
+
# Cache some attributes
|
88
|
+
ioPlugin.PossibleDestinations = ioPlugin.getPossibleDestinations
|
89
|
+
end
|
90
|
+
@Plugins.parsePluginsFromDir('Testers', "#{@RDILibDir}/Plugins/Testers", 'RDI::Testers') do |ioPlugin|
|
91
|
+
# Cache some attributes
|
92
|
+
ioPlugin.AffectingContextModifiers = ioPlugin.getAffectingContextModifiers
|
93
|
+
end
|
94
|
+
@Plugins.parsePluginsFromDir('Views', "#{@RDILibDir}/Plugins/Views", 'RDI::Views')
|
95
|
+
@Plugins.parsePluginsFromDir('ProgressViews', "#{@RDILibDir}/Plugins/ProgressViews", 'RDI::ProgressViews')
|
96
|
+
@Plugins.getPluginNames('Views').each do |iViewName|
|
97
|
+
@Plugins.parsePluginsFromDir("LocationSelectors_#{iViewName}", "#{@RDILibDir}/Plugins/Views/#{iViewName}/LocationSelectors", "RDI::Views::LocationSelectors::#{iViewName}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Set default options for ensuring dependencies.
|
102
|
+
# Options not specified in the given ones will not be overriden. To cancel existing values, set them explicitly to nil.
|
103
|
+
#
|
104
|
+
# Parameters:
|
105
|
+
# * *iParameters* (<em>map<Symbol,Object></em>): Additional parameters:
|
106
|
+
# ** *:AutoInstall* (_Integer_): When set to one of the DEST_* constants, RDI installs automatically to a location flagged with this constant, without the need of user choice [optional = nil]
|
107
|
+
# ** *:AutoInstallLocation* (_Object_): Used to provide the location to install to, when :AutoInstall is set to DEST_OTHER only.
|
108
|
+
# ** *:PossibleContextModifiers* (<em>map<String,list<list<[String,Object]>>></em>): The list of possible context modifiers sets to try, per dependency ID [optional = nil]
|
109
|
+
# ** *:PreferredViews* (<em>list<String></em>): The list of preferred views [optional = nil]
|
110
|
+
def setDefaultOptions(iParameters)
|
111
|
+
@DefaultOptions.merge!(iParameters)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Get the default location used by a given Installer for a given flavour
|
115
|
+
#
|
116
|
+
# Parameters:
|
117
|
+
# * *iInstallerName* (_String_): Name of the Installer plugin
|
118
|
+
# * *iFlavour* (_Integer_): Flavour required
|
119
|
+
# Return:
|
120
|
+
# * _Object_: Corresponding location, or LocationSelector name in case of DEST_OTHER flavour, or nil if none.
|
121
|
+
def getDefaultInstallLocation(iInstallerName, iFlavour)
|
122
|
+
rLocation = nil
|
123
|
+
|
124
|
+
accessPlugin('Installers', iInstallerName) do |iPlugin|
|
125
|
+
iPlugin.PossibleDestinations.each do |iDestInfo|
|
126
|
+
iDestFlavour, iDestLocation = iDestInfo
|
127
|
+
if (iDestFlavour == iFlavour)
|
128
|
+
rLocation = iDestLocation
|
129
|
+
break
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
return rLocation
|
135
|
+
end
|
136
|
+
|
137
|
+
# Ensure a given Location for a given ContextModifier
|
138
|
+
#
|
139
|
+
# Parameters:
|
140
|
+
# * *iContextModifierName* (_String_): Name of the context modifier
|
141
|
+
# * *iLocation* (_Location_): Location to add
|
142
|
+
def ensureLocationInContext(iContextModifierName, iLocation)
|
143
|
+
accessPlugin('ContextModifiers', iContextModifierName) do |ioPlugin|
|
144
|
+
if (!ioPlugin.isLocationInContext?(iLocation))
|
145
|
+
logDebug "Add Location #{iLocation} to #{iContextModifierName}."
|
146
|
+
ioPlugin.addLocationToContext(iLocation)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# The main method: ensure that a dependency is accessible
|
152
|
+
#
|
153
|
+
# Parameters:
|
154
|
+
# * *iDepDescList* (<em>list<DependencyDescription></em>): The list of dependencies's description to ensure
|
155
|
+
# * *iParameters* (<em>map<Symbol,Object></em>): Additional parameters:
|
156
|
+
# ** *:AutoInstall* (_Integer_): When set to one of the DEST_* constants, RDI installs automatically to a location flagged with this constant, without the need of user choice [optional = nil]
|
157
|
+
# ** *:AutoInstallLocation* (_Object_): Used to provide the location to install to, when :AutoInstall is set to DEST_OTHER only.
|
158
|
+
# ** *:PossibleContextModifiers* (<em>map<String,list<list<[String,Object]>>></em>): The list of possible context modifiers sets to try, per dependency ID [optional = nil]
|
159
|
+
# ** *:PreferredViews* (<em>list<String></em>): The list of preferred views [optional = nil]
|
160
|
+
# ** *:PreferredProgressViews* (<em>list<String></em>): The list of preferred progress views [optional = nil]
|
161
|
+
# Return:
|
162
|
+
# * _Exception_: The error, or nil in case of success
|
163
|
+
# * <em>map<String,list<[String,Object]>></em>: The list of context modifiers that have been applied to resolve the dependencies, per dependency ID (can be inconsistent in case of error)
|
164
|
+
# * <em>list<DependencyDescription></em>: The list of dependencies that were deliberately ignored (can be inconsistent in case of error)
|
165
|
+
# * <em>list<DependencyDescription></em>: The list of dependencies that could not be resolved (can be inconsistent in case of error)
|
166
|
+
def ensureDependencies(iDepDescList, iParameters = {})
|
167
|
+
rError = nil
|
168
|
+
rAppliedContextModifiers = {}
|
169
|
+
rIgnoredDeps = []
|
170
|
+
rUnresolvedDeps = []
|
171
|
+
|
172
|
+
lAutoInstall = @DefaultOptions[:AutoInstall]
|
173
|
+
if (iParameters.has_key?(:AutoInstall))
|
174
|
+
lAutoInstall = iParameters[:AutoInstall]
|
175
|
+
end
|
176
|
+
lAutoInstallLocation = @DefaultOptions[:AutoInstallLocation]
|
177
|
+
if (iParameters.has_key?(:AutoInstallLocation))
|
178
|
+
lAutoInstallLocation = iParameters[:AutoInstallLocation]
|
179
|
+
end
|
180
|
+
lPossibleContextModifiers = @DefaultOptions[:PossibleContextModifiers]
|
181
|
+
if (iParameters.has_key?(:PossibleContextModifiers))
|
182
|
+
lPossibleContextModifiers = iParameters[:PossibleContextModifiers]
|
183
|
+
end
|
184
|
+
lPreferredViews = @DefaultOptions[:PreferredViews]
|
185
|
+
if (iParameters.has_key?(:PreferredViews))
|
186
|
+
lPreferredViews = iParameters[:PreferredViews]
|
187
|
+
end
|
188
|
+
lPreferredProgressViews = @DefaultOptions[:PreferredProgressViews]
|
189
|
+
if (iParameters.has_key?(:PreferredProgressViews))
|
190
|
+
lPreferredProgressViews = iParameters[:PreferredProgressViews]
|
191
|
+
end
|
192
|
+
# First, test if the dependencies are already accessible
|
193
|
+
lDepsToResolve = []
|
194
|
+
iDepDescList.each do |iDepDesc|
|
195
|
+
if (!testDependency(iDepDesc))
|
196
|
+
lDepsToResolve << iDepDesc
|
197
|
+
end
|
198
|
+
end
|
199
|
+
if (!lDepsToResolve.empty?)
|
200
|
+
# Try resolving those dependencies with additional locations
|
201
|
+
lMissingDependencies = []
|
202
|
+
# Check if we can try some ContextModifiers that might find the missing dependencies
|
203
|
+
if (lPossibleContextModifiers == nil)
|
204
|
+
lMissingDependencies = lDepsToResolve
|
205
|
+
else
|
206
|
+
lMissingDependencies = getMissingDeps(lDepsToResolve, lPossibleContextModifiers, rAppliedContextModifiers)
|
207
|
+
end
|
208
|
+
if (!lMissingDependencies.empty?)
|
209
|
+
# If we ask for auto-installation, go on
|
210
|
+
if (lAutoInstall == nil)
|
211
|
+
# Ask the user what to do with those missing dependencies
|
212
|
+
rDepsUserChoices = askUserForMissingDeps(lMissingDependencies, lPreferredViews)
|
213
|
+
# Create a progression view
|
214
|
+
setupPreferredProgress(lPreferredProgressViews) do |ioProgressView|
|
215
|
+
# Be careful, as ioProgressView can be nil if no view was found
|
216
|
+
if (ioProgressView != nil)
|
217
|
+
ioProgressView.setRange(rDepsUserChoices.size)
|
218
|
+
end
|
219
|
+
# Parse what was returned by the user choices
|
220
|
+
rDepsUserChoices.each do |iDepUserChoice|
|
221
|
+
lDepDesc = iDepUserChoice.DepDesc
|
222
|
+
lIgnore = false
|
223
|
+
if (iDepUserChoice.Locate)
|
224
|
+
if (ioProgressView != nil)
|
225
|
+
ioProgressView.setTitle("Locate #{lDepDesc.ID}")
|
226
|
+
end
|
227
|
+
if (lDepDesc.Testers.size == iDepUserChoice.ResolvedTesters.size)
|
228
|
+
# This one was resolved using ContextModifiers.
|
229
|
+
# Apply them.
|
230
|
+
iDepUserChoice.ResolvedTesters.each do |iTesterName, iCMInfo|
|
231
|
+
iCMName, iCMContent = iCMInfo
|
232
|
+
accessPlugin('ContextModifiers', iCMName) do |ioPlugin|
|
233
|
+
ioPlugin.addLocationToContext(iCMContent)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
# Remember what we applied
|
237
|
+
rAppliedContextModifiers[lDepDesc.ID] = iDepUserChoice.ResolvedTesters.values
|
238
|
+
else
|
239
|
+
rUnresolvedDeps << lDepDesc
|
240
|
+
lIgnore = true
|
241
|
+
end
|
242
|
+
elsif (iDepUserChoice.IdxInstaller != nil)
|
243
|
+
if (ioProgressView != nil)
|
244
|
+
ioProgressView.setTitle("Install #{lDepDesc.ID}")
|
245
|
+
end
|
246
|
+
# This one is to be installed
|
247
|
+
# Get the installer plugin
|
248
|
+
lInstallerName, lInstallerContent, lContextModifiers = lDepDesc.Installers[iDepUserChoice.IdxInstaller]
|
249
|
+
accessPlugin('Installers', lInstallerName) do |ioInstallerPlugin|
|
250
|
+
lLocation = nil
|
251
|
+
if (ioInstallerPlugin.PossibleDestinations[iDepUserChoice.IdxDestination][0] == DEST_OTHER)
|
252
|
+
lLocation = iDepUserChoice.OtherLocation
|
253
|
+
else
|
254
|
+
lLocation = ioInstallerPlugin.PossibleDestinations[iDepUserChoice.IdxDestination][1]
|
255
|
+
end
|
256
|
+
lInstallEnv = {}
|
257
|
+
rError = installDependency(lDepDesc, iDepUserChoice.IdxInstaller, lLocation, lInstallEnv)
|
258
|
+
# Get what has been modified in the context
|
259
|
+
rAppliedContextModifiers[lDepDesc.ID] = lInstallEnv[:ContextModifiers]
|
260
|
+
# If an error occurred, cancel
|
261
|
+
if (rError != nil)
|
262
|
+
break
|
263
|
+
end
|
264
|
+
end
|
265
|
+
else
|
266
|
+
if (ioProgressView != nil)
|
267
|
+
ioProgressView.setTitle("Ignore #{lDepDesc.ID}")
|
268
|
+
end
|
269
|
+
rIgnoredDeps << lDepDesc
|
270
|
+
lIgnore = true
|
271
|
+
end
|
272
|
+
if (ioProgressView != nil)
|
273
|
+
ioProgressView.incValue
|
274
|
+
end
|
275
|
+
if (!lIgnore)
|
276
|
+
if (ioProgressView != nil)
|
277
|
+
ioProgressView.setTitle("Test #{lDepDesc.ID}")
|
278
|
+
end
|
279
|
+
# Test if it was installed correctly
|
280
|
+
if (!testDependency(lDepDesc))
|
281
|
+
# Still missing
|
282
|
+
rUnresolvedDeps << lDepDesc
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
else
|
288
|
+
# Create a progression view
|
289
|
+
setupPreferredProgress(lPreferredProgressViews) do |ioProgressView|
|
290
|
+
# Be careful, as ioProgressView can be nil if no view was found
|
291
|
+
if (ioProgressView != nil)
|
292
|
+
ioProgressView.setRange(lMissingDependencies.size)
|
293
|
+
end
|
294
|
+
lMissingDependencies.each do |iDepDesc|
|
295
|
+
if (ioProgressView != nil)
|
296
|
+
ioProgressView.setTitle("Installing #{iDepDesc.ID}")
|
297
|
+
end
|
298
|
+
# Try to find an installer and a location for the lAutoInstall value
|
299
|
+
lIdxInstaller = 0
|
300
|
+
lLocation = nil
|
301
|
+
iDepDesc.Installers.each do |iInstallerInfo|
|
302
|
+
iInstallerName, iInstallerContent, iContextModifiersList = iInstallerInfo
|
303
|
+
accessPlugin('Installers', iInstallerName) do |iPlugin|
|
304
|
+
iPlugin.PossibleDestinations.each do |iDestinationInfo|
|
305
|
+
iLocationFlavour, iLocation = iDestinationInfo
|
306
|
+
if (iLocationFlavour == lAutoInstall)
|
307
|
+
# We found it
|
308
|
+
if (iLocationFlavour == DEST_OTHER)
|
309
|
+
lLocation = lAutoInstallLocation
|
310
|
+
else
|
311
|
+
lLocation = iLocation
|
312
|
+
end
|
313
|
+
break
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
if (lLocation != nil)
|
318
|
+
break
|
319
|
+
end
|
320
|
+
lIdxInstaller += 1
|
321
|
+
end
|
322
|
+
if (lLocation == nil)
|
323
|
+
# We were unable to find a correct default location for lAutoInstall
|
324
|
+
rError = RuntimeError.new("Unable to find a default location for #{lAutoInstall}.")
|
325
|
+
else
|
326
|
+
lInstallEnv = {}
|
327
|
+
rError = installDependency(iDepDesc, lIdxInstaller, lLocation, lInstallEnv)
|
328
|
+
# Get what has been modified in the context
|
329
|
+
rAppliedContextModifiers[iDepDesc.ID] = lInstallEnv[:ContextModifiers]
|
330
|
+
# Test if it was installed correctly
|
331
|
+
if (!testDependency(iDepDesc))
|
332
|
+
# Still missing
|
333
|
+
rUnresolvedDeps << iDepDesc
|
334
|
+
end
|
335
|
+
end
|
336
|
+
if (rError != nil)
|
337
|
+
# An error occurred: cancel
|
338
|
+
break
|
339
|
+
end
|
340
|
+
if (ioProgressView != nil)
|
341
|
+
ioProgressView.incValue
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
return rError, rAppliedContextModifiers, rIgnoredDeps, rUnresolvedDeps
|
350
|
+
end
|
351
|
+
|
352
|
+
# Check if a dependency is ok
|
353
|
+
#
|
354
|
+
# Parameters:
|
355
|
+
# * *iDepDesc* (_DependencyDescription_): The dependency's description
|
356
|
+
# Return:
|
357
|
+
# * _Boolean_: Is the dependency accessible in our environment ?
|
358
|
+
def testDependency(iDepDesc)
|
359
|
+
rSuccess = true
|
360
|
+
|
361
|
+
# Loop among Testers
|
362
|
+
iDepDesc.Testers.each do |iTesterInfo|
|
363
|
+
iTesterName, iTesterContent = iTesterInfo
|
364
|
+
accessPlugin('Testers', iTesterName) do |iPlugin|
|
365
|
+
logDebug "Test dependency #{iTesterContent} using #{iTesterName} ..."
|
366
|
+
rSuccess = iPlugin.isContentResolved?(iTesterContent)
|
367
|
+
if (rSuccess)
|
368
|
+
logDebug "Dependency #{iTesterContent} found."
|
369
|
+
else
|
370
|
+
logDebug "Dependency #{iTesterContent} not found."
|
371
|
+
end
|
372
|
+
end
|
373
|
+
if (!rSuccess)
|
374
|
+
# This Tester answered false. Give up.
|
375
|
+
break
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
return rSuccess
|
380
|
+
end
|
381
|
+
|
382
|
+
# Install a given dependency on a given destination
|
383
|
+
#
|
384
|
+
# Parameters:
|
385
|
+
# * *iDepDesc* (_DependencyDescription_): The dependency's description
|
386
|
+
# * *iIdxInstaller* (_Integer_): Index of the installer to use in the description
|
387
|
+
# * *iLocation* (_Object_): The location to install to
|
388
|
+
# * *ioInstallEnvironment* (<em>map<Symbol,Object></em>): The install environment to fill [optional = {}]
|
389
|
+
# Return:
|
390
|
+
# * _Exception_: The exception, or nil in case of success
|
391
|
+
def installDependency(iDepDesc, iIdxInstaller, iLocation, ioInstallEnvironment = {})
|
392
|
+
rError = nil
|
393
|
+
|
394
|
+
# Get the installer information
|
395
|
+
iInstallerName, iInstallerContent, iContextModifiersList = iDepDesc.Installers[iIdxInstaller]
|
396
|
+
if (iInstallerName == nil)
|
397
|
+
logBug "Unable to get installer n.#{iIdxInstaller} in description #{iDepDesc.inspect}"
|
398
|
+
rError = RuntimeError.new("Unable to get installer n.#{iIdxInstaller} in description #{iDepDesc.inspect}")
|
399
|
+
else
|
400
|
+
# Install it
|
401
|
+
accessPlugin('Installers', iInstallerName) do |iPlugin|
|
402
|
+
logDebug "Install #{iInstallerContent} in #{iLocation} using #{iInstallerName} ..."
|
403
|
+
rError = iPlugin.installDependency(iInstallerContent, iLocation, ioInstallEnvironment)
|
404
|
+
if (rError == nil)
|
405
|
+
logDebug "Plugin #{iInstallerContent} installed correctly."
|
406
|
+
else
|
407
|
+
logDebug "Plugin #{iInstallerContent} installation ended in error: #{rError}."
|
408
|
+
end
|
409
|
+
end
|
410
|
+
if (rError == nil)
|
411
|
+
# Apply context modifiers
|
412
|
+
ioInstallEnvironment[:InstallLocation] = iLocation
|
413
|
+
# Store the context modifiers list in the environment, as it can be useful for further installations of the same dependency
|
414
|
+
ioInstallEnvironment[:ContextModifiers] = []
|
415
|
+
iContextModifiersList.each do |iContextModifierInfo|
|
416
|
+
iContextModifierName, iContextModifierContent = iContextModifierInfo
|
417
|
+
accessPlugin('ContextModifiers', iContextModifierName) do |iPlugin|
|
418
|
+
# Transform the content based on the installation environment
|
419
|
+
lContextContent = iPlugin.transformContentWithInstallEnv(iContextModifierContent, ioInstallEnvironment)
|
420
|
+
# Check if this content is already in the context
|
421
|
+
if (!iPlugin.isLocationInContext?(lContextContent))
|
422
|
+
# We have to add it.
|
423
|
+
logDebug "Add #{lContextContent} to context #{iContextModifierName}"
|
424
|
+
iPlugin.addLocationToContext(lContextContent)
|
425
|
+
end
|
426
|
+
ioInstallEnvironment[:ContextModifiers] << [ iContextModifierName, lContextContent ]
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
return rError
|
433
|
+
end
|
434
|
+
|
435
|
+
# Get access to one of RDI's plugins.
|
436
|
+
# An exception is thrown if the plugin does not exist.
|
437
|
+
# Used by:
|
438
|
+
# * Regression
|
439
|
+
# * Views
|
440
|
+
#
|
441
|
+
# Parameters:
|
442
|
+
# * *iCategoryName* (_String_): Category of the plugin to access
|
443
|
+
# * *iPluginName* (_String_): Name of the plugin to access
|
444
|
+
# * *CodeBlock*: The code called when the plugin is found:
|
445
|
+
# ** *ioPlugin* (_Object_): The corresponding plugin
|
446
|
+
def accessPlugin(iCategoryName, iPluginName)
|
447
|
+
@Plugins.accessPlugin(iCategoryName, iPluginName, :RDIInstaller => self) do |ioPlugin|
|
448
|
+
yield(ioPlugin)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# Get list of plugins for a given category.
|
453
|
+
# Used by:
|
454
|
+
# * Regression
|
455
|
+
#
|
456
|
+
# Parameters:
|
457
|
+
# * *iCategoryName* (_String_): Category of the plugin to access
|
458
|
+
# Return:
|
459
|
+
# * <em>list<String></em>: List of plugin names
|
460
|
+
def getPluginNames(iCategoryName)
|
461
|
+
return @Plugins.getPluginNames(iCategoryName)
|
462
|
+
end
|
463
|
+
|
464
|
+
# Register a new plugin
|
465
|
+
# Used by:
|
466
|
+
# * Regression
|
467
|
+
#
|
468
|
+
# Parameters:
|
469
|
+
# * *iCategoryName* (_String_): Category this plugin belongs to
|
470
|
+
# * *iPluginName* (_String_): Plugin name
|
471
|
+
# * *iFileName* (_String_): File name containing the plugin (can be nil)
|
472
|
+
# * *iDesc* (<em>map<Symbol,Object></em>): Plugin's description (can be nil)
|
473
|
+
# * *iClassName* (_String_): Name of the plugin class
|
474
|
+
# * *iInitCodeBlock* (_Proc_): Code block to call when initializing the real instance (can be nil)
|
475
|
+
def registerNewPlugin(iCategoryName, iPluginName, iFileName, iDesc, iClassName, iInitCodeBlock)
|
476
|
+
@Plugins.registerNewPlugin(iCategoryName, iPluginName, iFileName, iDesc, iClassName, iInitCodeBlock)
|
477
|
+
end
|
478
|
+
|
479
|
+
# Get the Main instance, if there is one defined, or nil otherwise
|
480
|
+
#
|
481
|
+
# Return:
|
482
|
+
# * _Installer_: The main instance, or nil if none.
|
483
|
+
def self.getMainInstance
|
484
|
+
return @@MainInstallerInstance
|
485
|
+
end
|
486
|
+
|
487
|
+
# == Private API ==
|
488
|
+
|
489
|
+
private
|
490
|
+
|
491
|
+
# The main RDI Installer instance
|
492
|
+
# Installer
|
493
|
+
@@MainInstallerInstance = nil
|
494
|
+
|
495
|
+
# Get the missing dependencies after trying previously applied context modifiers
|
496
|
+
#
|
497
|
+
# Parameters:
|
498
|
+
# * *iDepsToResolve* (<em>list<DependencyDescription></em>): The list of dependencies to resolve
|
499
|
+
# * *iPossibleContextModifiers* (<em>map<String,list<list<[String,Object]>>></em>): The list of possible context modifiers sets to try, per dependency ID
|
500
|
+
# * *ioAppliedContextModifiers* (<em>map<String,list<[String,Object]>></em>): The list of context modifiers that have been applied to resolve the dependencies, per dependency ID
|
501
|
+
# Return:
|
502
|
+
# * <em>list<DependencyDescription></em>: The list of dependencies that are still missing
|
503
|
+
def getMissingDeps(iDepsToResolve, iPossibleContextModifiers, ioAppliedContextModifiers)
|
504
|
+
rMissingDeps = []
|
505
|
+
|
506
|
+
# We might have some install environments to try
|
507
|
+
iDepsToResolve.each do |iDepDesc|
|
508
|
+
lDepID = iDepDesc.ID
|
509
|
+
lCMListsToTry = iPossibleContextModifiers[lDepID]
|
510
|
+
lDepResolved = false
|
511
|
+
if (lCMListsToTry != nil)
|
512
|
+
# We can try several ones. We want at least 1 of those sets to resolve the dependency.
|
513
|
+
lCMListsToTry.each do |iCMSetToTry|
|
514
|
+
# Try to add the locations to the context
|
515
|
+
lAppliedCMs = []
|
516
|
+
iCMSetToTry.each do |iContextModifierInfo|
|
517
|
+
iName, iContent = iContextModifierInfo
|
518
|
+
accessPlugin('ContextModifiers', iName) do |ioPlugin|
|
519
|
+
if (!ioPlugin.isLocationInContext?(iContent))
|
520
|
+
# We try this one.
|
521
|
+
ioPlugin.addLocationToContext(iContent)
|
522
|
+
lAppliedCMs << iContextModifierInfo
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
# Now, test if this has resolved the dependency (don't try if nothing changed)
|
527
|
+
if (!lAppliedCMs.empty?)
|
528
|
+
lDepResolved = testDependency(iDepDesc)
|
529
|
+
# If we found it, it's ok
|
530
|
+
if (lDepResolved)
|
531
|
+
ioAppliedContextModifiers[iDepDesc.ID] = lAppliedCMs
|
532
|
+
break
|
533
|
+
else
|
534
|
+
# Rollback those context modifications as they were useless
|
535
|
+
lAppliedCMs.each do |iContextModifierInfo|
|
536
|
+
iName, iContent = iContextModifierInfo
|
537
|
+
accessPlugin('ContextModifiers', iName) do |ioPlugin|
|
538
|
+
ioPlugin.removeLocationFromContext(iContent)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end
|
545
|
+
# If none of them resolved the dependency, add the dependency to the missing ones
|
546
|
+
if (!lDepResolved)
|
547
|
+
rMissingDeps << iDepDesc
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
return rMissingDeps
|
552
|
+
end
|
553
|
+
|
554
|
+
# Ask the user about missing dependencies.
|
555
|
+
# This method will use a user interface to know what to do with missing dependencies.
|
556
|
+
# For each dependency, choices are:
|
557
|
+
# * Install it
|
558
|
+
# * Ignore it
|
559
|
+
# * Change the context to find it (select directory...)
|
560
|
+
# Views are used to ask the user for those.
|
561
|
+
# It is possible to specify a list of preferred views. If it is the case, RDI will try first views among this list that already have their dependencies accessible, then RDI will try to install the dependencies of the first one. It will use the first view it can.
|
562
|
+
# If not specified, RDI will use arbitrary the first view it can, and eventualy try to install one.
|
563
|
+
#
|
564
|
+
# Parameters:
|
565
|
+
# * *iMissingDependencies* (<em>list<DependencyDescription></em>): The missing dependencies list
|
566
|
+
# * *iPreferredViewsList* (<em>list<String></em>): The list of preferred views (can be nil)
|
567
|
+
# Return:
|
568
|
+
# * <em>list<DependencyUserChoice></em>: The list of dependencies user choices
|
569
|
+
def askUserForMissingDeps(iMissingDependencies, iPreferredViewsList)
|
570
|
+
rDependenciesUserChoices = []
|
571
|
+
|
572
|
+
lViewsList = iPreferredViewsList
|
573
|
+
if ((iPreferredViewsList == nil) or
|
574
|
+
(iPreferredViewsList.empty?))
|
575
|
+
# Set all views as being preferred
|
576
|
+
lViewsList = @Plugins.getPluginNames('Views')
|
577
|
+
end
|
578
|
+
if (lViewsList.empty?)
|
579
|
+
logBug 'No view was accessible among plugins. Please check your Plugin/Views directory.'
|
580
|
+
else
|
581
|
+
# Now we try to select 1 view that is accessible without any dependency installation
|
582
|
+
lPlugin = nil
|
583
|
+
lViewsList.each do |iViewName|
|
584
|
+
lPlugin, lError = @Plugins.getPluginInstance('Views', iViewName,
|
585
|
+
:OnlyIfExtDepsResolved => true,
|
586
|
+
:RDIInstaller => self
|
587
|
+
)
|
588
|
+
if (lPlugin != nil)
|
589
|
+
# Found one
|
590
|
+
logDebug "Executing View #{iViewName}"
|
591
|
+
break
|
592
|
+
end
|
593
|
+
end
|
594
|
+
if (lPlugin == nil)
|
595
|
+
# Now we try to install them
|
596
|
+
lViewsList.each do |iViewName|
|
597
|
+
lPlugin, lError = @Plugins.getPluginInstance('Views', iViewName,
|
598
|
+
:RDIInstaller => self
|
599
|
+
)
|
600
|
+
if (lPlugin != nil)
|
601
|
+
# Found one
|
602
|
+
logDebug "Executing View #{iViewName}"
|
603
|
+
break
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
if (lPlugin == nil)
|
608
|
+
logBug 'After trying all preferred views, we are still unable to have one.'
|
609
|
+
else
|
610
|
+
# Call it
|
611
|
+
rDependenciesUserChoices = lPlugin.execute(self, iMissingDependencies)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
return rDependenciesUserChoices
|
616
|
+
end
|
617
|
+
|
618
|
+
# Setup a progression view based on user preferences.
|
619
|
+
# It is possible to specify a list of preferred progress views. If it is the case, RDI will try first views among this list that already have their dependencies accessible, then RDI will try to install the dependencies of the first one. It will use the first view it can.
|
620
|
+
# If not specified, RDI will use arbitrary the first progress view it can, and eventualy try to install one.
|
621
|
+
#
|
622
|
+
# Parameters:
|
623
|
+
# * *iPreferredProgressViewsList* (<em>list<String></em>): The list of preferred views (can be nil)
|
624
|
+
# * _CodeBlock_: Code to execute once the progress view has been found
|
625
|
+
# ** *ioProgressView* (_Object_): The corresponding progress view
|
626
|
+
def setupPreferredProgress(iPreferredProgressViewsList)
|
627
|
+
lViewsList = iPreferredProgressViewsList
|
628
|
+
if ((iPreferredProgressViewsList == nil) or
|
629
|
+
(iPreferredProgressViewsList.empty?))
|
630
|
+
# Set all views as being preferred
|
631
|
+
lViewsList = @Plugins.getPluginNames('ProgressViews')
|
632
|
+
end
|
633
|
+
if (lViewsList.empty?)
|
634
|
+
logDebug 'No progress view was accessible among plugins. Please check your Plugin/ProgressViews directory. Continuing without progress view.'
|
635
|
+
else
|
636
|
+
# Now we try to select 1 progress view that is accessible without any dependency installation
|
637
|
+
lPlugin = nil
|
638
|
+
lViewsList.each do |iViewName|
|
639
|
+
lPlugin, lError = @Plugins.getPluginInstance('ProgressViews', iViewName,
|
640
|
+
:OnlyIfExtDepsResolved => true,
|
641
|
+
:RDIInstaller => self
|
642
|
+
)
|
643
|
+
if (lPlugin != nil)
|
644
|
+
# Found one
|
645
|
+
logDebug "Executing Progress View #{iViewName}"
|
646
|
+
break
|
647
|
+
end
|
648
|
+
end
|
649
|
+
if (lPlugin == nil)
|
650
|
+
# Now we try to install them
|
651
|
+
lViewsList.each do |iViewName|
|
652
|
+
lPlugin, lError = @Plugins.getPluginInstance('ProgressViews', iViewName,
|
653
|
+
:RDIInstaller => self
|
654
|
+
)
|
655
|
+
if (lPlugin != nil)
|
656
|
+
# Found one
|
657
|
+
logDebug "Executing Progress View #{iViewName}"
|
658
|
+
break
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
if (lPlugin == nil)
|
663
|
+
logDebug 'After trying all preferred progress views, we are still unable to have one. Performing without progression.'
|
664
|
+
end
|
665
|
+
end
|
666
|
+
# Call it
|
667
|
+
if (lPlugin == nil)
|
668
|
+
yield(nil)
|
669
|
+
else
|
670
|
+
lPlugin.setupProgress do |ioProgressView|
|
671
|
+
yield(ioProgressView)
|
672
|
+
end
|
673
|
+
end
|
674
|
+
end
|
675
|
+
|
676
|
+
end
|
677
|
+
|
678
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module RDI
|
7
|
+
|
8
|
+
module Model
|
9
|
+
|
10
|
+
class ContextModifier
|
11
|
+
|
12
|
+
# The name of the LocationSelector class
|
13
|
+
# String
|
14
|
+
attr_accessor :LocationSelectorName
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|