我有一个带有Parallel Toolbox的MATLAB R2016b的独立许可证。我正在编写一个应用程序,该应用程序将发送给可能没有安装该工具箱的客户,并且我想编写我的应用程序以使其正常失败(即运行单线程)。问题是,由于我安装了工具箱,当我尝试使用并行工具箱功能时,我从未收到错误,所以我无法测试失败点。
为了概括这种情况,我希望能够在不满足工具箱依赖性的情况下测试应用程序的行为,这样我就可以向用户提供有意义的消息,告诉他们为什么他们不能使用我的应用程序或者其他提供解决方法。
有没有办法,没有重复卸载然后重新安装工具箱,或者购买第二个许可证并在第二台计算机(或使用不同用户名的同一台计算机)上安装,暂时让MATLAB认为它不是'可以吗?
从评论到问题,似乎我的问题的答案是“不”。只需删除路径不会禁用工具箱。首先,工具箱不在一个目录中,它位于几个(我必须为{Toolerbox}的rmpath
11个目录中,我真的不知道我是否全部使用它们。
完成后,输入ver
仍然显示已安装Parallel Toolbox。它还会抛出不同的错误,因此该方法不能用于充分测试程序中的行为。例如,在我删除目录后,我得到以下行为:
>> n = gpuDeviceCount
Undefined function or variable 'gpuDeviceCount'.
但是当我问一个没有安装并行工具箱的朋友输入相同的命令时,他得到了以下内容:
>> gpuDeviceCount
gpuDeviceCount is not included in your installed products. These products offer 'gpuDeviceCount':
Parallel Computing Toolbox
不同的错误,抛出不同的异常。第二个是预期的,如果我可以轻易地并故意抛出该错误,我可以看到在try / catch块中测试的异常并且行为恰当。第一个是出乎意料的,并且可能由于任何原因而发生,例如我的安装损坏或愚蠢的错误(例如,添加了路径,n = gpuDeviceCounts;
仍然会抛出第一个错误。)
我会向MathWorks提交一张票,如果我收到回复,我会更新问题,除非有人有另一种解决方法。
答案 0 :(得分:3)
完成。
我会在FEX上发布此内容,在获得批准后,我会在此处放置链接。
编辑1 他们批准了我的提交,可以here或直接从my GitHub找到。 我将从这篇帖子中删除代码。(实际上,我会留下来,因为这会把答案变成一个仅链接的回复,正如Andy Clifton所说的那样)。
我在R2010a上测试了这个 ,它通过了我所有的单元测试。但是经验表明MATLAB可能会在版本之间做很多不同的事情,所以如果你发现问题,得到一个奇怪的错误等,请告诉我,所以我可以进行修复。
目前,这是版本1.0:
% TOGGLETOOLBOX Utility to switch MATLAB toolboxes on or off.
%
% S = TOGGLETOOLBOX()
% S = TOGGLETOOLBOX('')
% S = TOGGLETOOLBOX('all') queries the on/off states of all installed
% toolboxes.
%
% M = TOGGLETOOLBOX('names') returns the full names / directory names map
% [M] applicable to the current MATLAB installation.
%
% S = TOGGLETOOLBOX(toolbox, state) queries or sets the on/off state of the
% MATLAB toolbox [toolbox] to [state]. The string or cellstring [toolbox] may
% be equal to the toolbox' installation directory name (the same as used by
% ver()), or the toolbox' full name. The string [state] may be one of 'on',
% 'off' or 'query'. The return argument [S] is a structure containing the
% toolbox name(s) as fields, with the on/off state represented as true/false.
%
% S = TOGGLETOOLBOX(..., permanency) for string [permanency] equal to
% 'permanent' will attempt to make the change persist between different
% MATLAB sessions. For [permanency] equal to 'temporary' (the default), the
% change will only last for the remainder of the current session.
%
% TOGGLETOOLBOX(S0) will reset the on/off states of all toolboxes to the
% states contained in [S0], where [S0] is a structure previously returned by
% TOGGLETOOLBOX() as outlined above.
%
% Disabling a toolbox is done by removing the relevant directories from the
% MATLAB path. Since the order of the path is important for name resolution,
% TOGGLETOOLBOX() attempts to keep the order of all paths as close to MATLAB's
% startup path as possible. Calling TOGGLETOOLBOX() multiple times for different
% toolboxes and arbitrary on/off states should not affect the overall path
% order -- calling TOGGLETOOLBOX('all', 'on') afterwards results in a path
% identical to the startup path.
%
% Note that TOGGLETOOLBOX() generates a MAT file for both performance and
% permanence between MATLAB sessions. Please make sure that TOGGLETOOLBOX()
% is located in a directory with write access.
%
%
% EXAMPLE SESSION:
%
% >> M = toggleToolbox('names')%
% M =
% 'aero' 'Aerospace Toolbox'
% 'aeroblks' 'Aerospace Blockset'
% 'bioinfo' 'Bioinformatics Toolbox'
% 'comm' 'Communications Toolbox'
% ...
%
% >> S = toggleToolbox({'Aerospace Toolbox' 'Wavelet Toolbox'}, 'query')
% S =
% aero: 1
% wavelet: 1
%
% >> w = ver('wavelet')
% w =
% Name: 'Wavelet Toolbox'
% Version: '4.5'
% Release: '(R2010a)'
% Date: '25-Jan-2010'
%
% >> S = toggleToolbox({'Aerospace Toolbox' 'Wavelet Toolbox'}, 'off');
% >> toggleToolbox({'Aerospace Toolbox' 'Wavelet Toolbox'}, 'query')
% ans =
% aero: 0
% wavelet: 0
%
% >> w = ver('wavelet')
% w =
% 0x0 struct array with fields:
% Name
% Version
% Release
% Date
%
% >> toggleToolbox(S);
% >> toggleToolbox({'Aerospace Toolbox' 'Wavelet Toolbox'}, 'query')
% ans =
% aero: 1
% wavelet: 1
%
% >> % Cross-platform developer mode:
% >> S = toggleToolbox('all', 'off');
%
% See also ver, verLessThan, matlabroot, warning.
function varargout = toggleToolbox(varargin)
%% Initialize
% ====================================================
% Default msg ID for error/warning messages
msgId = mfilename();
% Store toolbox states in a store file
storefile = fullfile(fileparts(mfilename('fullpath')), ...
'toolbox_states.mat');
% Names should be given as directory names, but who on Earth
% knows those by heart? Therefore, we create a dirname/fullname
% map, to allow users to enter the full toolbox name as well
tb_name_map = get_tb_name_map();
% Parse and check arguments to determine mode of operation
restoremode = false;
querymode = false;
toolbox = 'all';
state = 'on';
permanent = 'temporary';
switch nargin
case 0
% return states of ALL toolboxes
querymode = true;
case 1
% Reset states
if isstruct(varargin{1})
restoremode = true;
state = 'restore';
toolbox_states = varargin{1};
assert(nargout == 0,...
[msgId ':argoutcount_error'], ...
'%s for single input argument does not have any output arguments.',...
mfilename);
assert(isfield(toolbox_states, 'path') && ...
all(isfield(toolbox_states, tb_name_map(:,1))),...
[msgId ':invalid_tbstates_structure'], ...
'Input argument does not appear to be a structure generated by %s.',...
mfilename);
% Query state of single toolbox
elseif ischar(varargin{1})
% Return toolbox names map
if strcmpi(varargin{1}, 'names')
varargout{1} = tb_name_map;
return;
% query mode
else
querymode = true;
toolbox = varargin{1};
state = 'query';
end
end
case 2
% Toggle state of one or more toolboxes
toolbox = varargin{1};
state = varargin{2};
querymode = strcmpi(state, 'query');
case 3
toolbox = varargin{1};
state = varargin{2};
querymode = strcmpi(state, 'query');
if ~querymode
permanent = varargin{3};
else
warning([msgId ':permanence_na_in_querymode'],...
'Permanency flag ignored for ''query'' mode.');
end
otherwise
error([msgId ':argincount_error'],...
'Too many input arguments.');
end
% Cellstring of all paths makes for easier work
paths = regexp(path, pathsep, 'split');
% Initialize tb states structure
if ~restoremode
if exist(storefile,'file')==2
% Load previous paths and toggle states, if any
S = load(storefile);
toolbox_states = S.toolbox_states;
clear S;
else
% Otherwise, just initialize it by storing the previous
% path strings...
toolbox_states = struct('path', {paths});
% ...and mark all toolboxes as "enabled"
for ii = 1:size(tb_name_map,1)
toolbox_states.(tb_name_map{ii,1}) = true; end
end
end
% Some asserts
assert(iscellstr(toolbox) || ischar(toolbox),...
[msgId ':argument_error'], [...
'Toolboxes must be given as a string (single toolbox) or a cell array of ',...
'strings (multiple toolboxes).']);
if ~restoremode
assert(ischar(state) && any(strcmpi(state, {'on' 'off' 'query'})),...
[msgId ':argument_error'],...
'State must be a string equal to ''on'', ''off'' or ''query''.');
end
assert(ischar(permanent) && any(strcmpi(permanent, {'permanent', 'temporary'})),...
[msgId ':argument_error'],...
'Permanency must be indicated via string ''perpanent'' or ''temporary''.');
% Apply name map
if isempty(toolbox) || any(strcmpi(toolbox, 'all'))
toolbox = tb_name_map(:,1);
else
if ~iscell(toolbox)
toolbox = {toolbox}; end
% Check names and perform lookups
tb_name_map_i = cellfun(@lower, tb_name_map, 'UniformOutput', false);
toolbox_i = cellfun(@lower, toolbox, 'UniformOutput', false);
dirs = ismember(toolbox_i, tb_name_map_i(:,1));
[isname,names] = ismember(toolbox_i, tb_name_map_i(:,2));
assert(all(dirs | isname),...
[msgId ':unknown_toolbox'],...
'Toolbox: ''%s'' does not seem to be installed.',...
toolbox{find( ~(dirs | isname), 1, 'first')});
toolbox(isname) = tb_name_map(names(isname),1);
end
% Query mode: return current toggle states
if querymode
% ALL toolboxes
if isempty(toolbox)
save(storefile, 'toolbox_states');
%toolbox_states = rmfield(toolbox_states, 'path');
varargout{1} = toolbox_states;
% SOME toolboxes
else
for ii = 1:numel(toolbox)
toolbox_state.(toolbox{ii}) = toolbox_states.(toolbox{ii}); end
varargout{1} = toolbox_state;
end
return;
end
%% Toggle all requested toolboxes
% ====================================================
toolbox_states_out = toolbox_states;
switch lower(state)
case 'off'
for ii = 1:numel(toolbox)
tb = toolbox{ii};
if isfield(toolbox_states, tb) && ~toolbox_states.(tb)
warning([msgId ':toolbox_already_switched_off'],...
'Toolbox ''%s'' already switched off; ignoring.',...
tb);
else
% Remove whole toolbox from path
toolbox_states.(tb) = false;
inds = ~cellfun('isempty', strfind(paths, fullfile(matlabroot, 'toolbox', tb)));
rmpath(paths{inds});
end
end
case {'on' 'restore'}
switched = false;
for ii = 1:numel(toolbox)
tb = toolbox{ii};
if ~isfield(toolbox_states, toolbox{ii})
warning([msgId ':toolbox_not_switched_off'],...
'Toolbox ''%s'' was not switched off; ignoring.',...
tb);
else
switched = true;
end
% Just set toggle state
toolbox_states.(tb) = true;
end
% Preserve path order:
% - restore whole path
% - then re-disable relevant toolboxes
if switched
newPaths = ~ismember(paths, toolbox_states.path);
if ~any(newPaths)
% No new paths have been added
path( sprintf('%s;', toolbox_states.path{1:end-1}),...
toolbox_states.path{end} );
paths = toolbox_states.path;
else
% TODO: wouldn't it be better to "simply" merge the paths?
warning([msgId ':new_paths_added'],[ ...
'New directories have been added to the path between consecutive ',...
'''on''/''off'' calls to %s. The path will be restored to that prior to ',...
'the first ''off'' call to %s, which will effectively remove these ',...
'new directories from the path.'],...
mfilename, mfilename);
end
toolboxes = fieldnames(toolbox_states);
toolboxes = toolboxes(~strcmp(toolboxes, 'path'));
for jj = 1:numel(toolboxes)
tb = toolboxes{jj};
if ~toolbox_states.(tb)
inds = ~cellfun('isempty', strfind(paths, fullfile(matlabroot, 'toolbox', tb)));
rmpath(paths{inds});
end
end
end
end
%% Finish up
% ====================================================
% Save toggle states and previous paths
save(storefile, 'toolbox_states');
% Make changes permanent when requested
switch lower(permanent)
case 'temporary'
% noop; the default
case 'permanent'
savepath();
end
if ~restoremode
varargout{1} = toolbox_states_out; end
end
function tb_name_map = get_tb_name_map()
persistent tb_map
if isempty(tb_map)
disp('First call; collecting toolbox information. Please wait...');
% Cellstring of all paths makes for easier work
paths = regexp(path, pathsep, 'split');
% Get list of toolbox directory names
tbs = dir(fullfile(matlabroot, 'toolbox'));
tbs = tbs(3:end);
tb_dirnames = {tbs.name}';
% Get the full toolbox names
S(numel(tb_dirnames)) = struct('Name', '',...
'Version', '',...
'Release', '',...
'Date', '');
for ii = 1:numel(tb_dirnames)
tb_dirname = tb_dirnames{ii};
% Add toolbox (could have been removed from path, in which case
% ver() will not find it)
tb_dir = fullfile(matlabroot, 'toolbox', tb_dirname);
inds = ~cellfun('isempty', ...
strfind(paths, tb_dir));
if ~any(inds)
addpath(genpath(tb_dir)); end
% Get toolbox information via ver()
tb_version = ver(tb_dirname);
if ~isempty(tb_version)
S(ii) = tb_version; end
end
tb_fullnames = {S.Name}';
% Some dirs may not be toolboxes; slice those off
tb_slice = ~cellfun('isempty', tb_fullnames);
tb_fullnames = tb_fullnames(tb_slice);
tb_dirnames = tb_dirnames (tb_slice);
% Collect terms
tb_map = [tb_dirnames tb_fullnames];
% Reset path
path(sprintf('%s;', paths{1:end-1}), paths{end});
end
tb_name_map = tb_map;
end