Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/badges/code_issues.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/badges/tests.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 21 additions & 2 deletions code/+matbox/+setup/+internal/installFexPackage.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@
options.AddToPath (1,1) logical = true
options.AddToPathWithSubfolders (1,1) logical = true
options.Verbose (1,1) logical = true
options.AgreeToLicense (1,1) logical = false
% No default, so an explicit false can be told from an omitted value
options.AgreeToLicense (1,1) logical
end

isLicenseOptionGiven = isfield(options, "AgreeToLicense");
isLicenseAgreed = isLicenseOptionGiven && options.AgreeToLicense;
isLicenseDeclined = isLicenseOptionGiven && ~options.AgreeToLicense;

% Check if toolbox is installed
[isInstalled, version, toolboxFolder] = matbox.setup.internal.fex.isToolboxInstalled(toolboxIdentifier, options.Version);

Expand Down Expand Up @@ -101,11 +106,25 @@

elseif endsWith(addonUrl, '/mltbx')
[tempFilepath, C] = matbox.setup.internal.utility.tempsave(addonUrl, [toolboxIdentifier, '_temp.mltbx']);
installedAddon = matlab.addons.install(tempFilepath, options.AgreeToLicense);
isLicenseOptionSupported = isMATLABReleaseOlderThan("R2026b");
if isLicenseOptionSupported
installedAddon = matlab.addons.install(tempFilepath, isLicenseAgreed);
else
% From R2026b, installing accepts the license agreement,
% and an agreeToLicense argument of false is an error.
installedAddon = matlab.addons.install(tempFilepath);
end
if isempty(installedAddon)
fprintf(newline)
error('Failed to install "%s"...', toolboxName)
end
if isLicenseDeclined && ~isLicenseOptionSupported
fprintf(newline)
warning("MatBox:Setup:AgreeToLicenseIgnored", ...
['AgreeToLicense is false, but from R2026b MATLAB accepts the ', ...
'license agreement when it installs an add-on. "%s" was installed ', ...
'and its license terms apply.'], toolboxName)
end
packageTargetFolder = string(missing);
installationMethod = "mltbx";
end
Expand Down
11 changes: 9 additions & 2 deletions code/+matbox/+setup/installFromSourceUri.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@
options.AddToPath (1,1) logical = true
options.Update (1,1) logical = false
options.Verbose (1,1) logical = true
options.AgreeToLicense (1,1) logical = false
options.AgreeToLicense (1,1) logical
end

if startsWith(sourceUri, "fex://")
% AgreeToLicense has no default, so the installer can tell an
% explicit choice from an omitted one. Forward it only when given.
licenseArgs = {};
if isfield(options, "AgreeToLicense")
licenseArgs = {"AgreeToLicense", options.AgreeToLicense};
end

[packageUuid, title, version] = getFEXPackageSpecification(sourceUri);
[packageTargetFolder, installationType] = matbox.setup.internal.installFexPackage( ...
packageUuid, ...
Expand All @@ -35,7 +42,7 @@
"Version", version, ...
"AddToPath", options.AddToPath, ...
"Verbose", options.Verbose, ...
"AgreeToLicense", options.AgreeToLicense);
licenseArgs{:});

installResult = struct( ...
"FilePath", packageTargetFolder, ...
Expand Down
11 changes: 9 additions & 2 deletions code/+matbox/installRequirements.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,20 @@ function installRequirements(toolboxFolder, mode, options)
options.SaveSearchPath (1,1) logical = true
options.InstallationLocation (1,1) string = matbox.setup.internal.getDefaultAddonFolder()
options.Verbose (1,1) logical = true
options.AgreeToLicenses (1,1) logical = false
options.AgreeToLicenses (1,1) logical
end

% Parse mode/flags
mode = string(mode);
doUpdate = any(strcmp(mode, 'update')) || any(strcmp(mode, 'u'));

% AgreeToLicenses has no default, so the installer can tell an explicit
% choice from an omitted one. Forward it only when given.
licenseArgs = {};
if isfield(options, "AgreeToLicenses")
licenseArgs = {"AgreeToLicense", options.AgreeToLicenses};
end

installationLocation = options.InstallationLocation;
if ~isfolder(installationLocation); mkdir(installationLocation); end

Expand All @@ -32,7 +39,7 @@ function installRequirements(toolboxFolder, mode, options)
"AddToPath", options.UpdateSearchPath, ...
"Update", doUpdate, ...
"Verbose", options.Verbose, ...
"AgreeToLicense", options.AgreeToLicenses);
licenseArgs{:});
case 'Unknown'
continue
end
Expand Down
40 changes: 40 additions & 0 deletions tools/tests/+matboxtools/+unittest/SetupTest.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
classdef SetupTest < matlab.unittest.TestCase
% BasicTest - Unit test for testing the openMINDS tutorials.

properties (Constant)
% A FileExchange package that is distributed as an .mltbx file
FexMltbxUri = "fex://160058-recursively-list-files-and-folders"
end

methods (TestClassSetup)
function setupClass(testCase) %#ok<*MANU>
% pass
Expand Down Expand Up @@ -35,9 +40,44 @@ function testUpdateGitRequirementWithGit(testCase)
testCase.installRequirements("Update", true)
rmpath(genpath(fullfile(pwd, "StructEditor")))
end

function testExplicitAgreeToLicenseFalseWarns(testCase)
testCase.assumeFalse(isMATLABReleaseOlderThan("R2026b"), ...
"MATLAB ignores a declined add-on license only from R2026b.")
testCase.uninstallFexAddon(testCase.FexMltbxUri)

testCase.verifyWarning( ...
@() matbox.setup.installFromSourceUri(testCase.FexMltbxUri, ...
"InstallationLocation", pwd, "AgreeToLicense", false), ...
"MatBox:Setup:AgreeToLicenseIgnored")
end

function testOmittedAgreeToLicenseDoesNotWarn(testCase)
testCase.assumeFalse(isMATLABReleaseOlderThan("R2026b"), ...
"MATLAB ignores a declined add-on license only from R2026b.")
testCase.uninstallFexAddon(testCase.FexMltbxUri)

% No warning follows the license warning in the install path,
% so the last warning shows whether it was issued.
lastwarn("", "")
matbox.setup.installFromSourceUri(testCase.FexMltbxUri, "InstallationLocation", pwd)
[~, lastWarningId] = lastwarn();
testCase.verifyNotEqual(lastWarningId, 'MatBox:Setup:AgreeToLicenseIgnored')
end
end

methods
function uninstallFexAddon(~, sourceUri)
% installFexPackage skips an add-on that is already installed, so
% remove every installed version to make the next call install it.
addonIdentifier = matbox.setup.internal.fex.parseFileExchangeURI(sourceUri);
addons = matlab.addons.installedAddons();
addonVersions = addons.Version(addons.Identifier == addonIdentifier);
for addonVersion = reshape(addonVersions, 1, [])
matlab.addons.uninstall(addonIdentifier, addonVersion)
end
end

function installRequirements(testCase, options)

arguments
Expand Down