-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cleanup and modernize the wrapper #52
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
4a93044
Reorganize OSQP class structure
imciner2 e6a2ca8
Expose rho update function over the mex interface
imciner2 2f5aba3
Simplify the constant generation
imciner2 c3d24fc
Only compile the memory management functions once then link them
imciner2 cc96cf2
Restructure settings handling to make it easier to maintain
imciner2 a6d5c70
Fix copying OSQP Settings objects and add better update tests
imciner2 7b7f38f
[CI] Update OSQP Mex build step
imciner2 5e42763
Refactor how structs are passed to allow cleaner passing of OSQPInfo
imciner2 8025d54
Rework how vectors are created and passed around
imciner2 424bccd
Cleanup unused routines and some style changes
imciner2 5cf4a61
Remove extraneous static string on mex call to static methods
imciner2 b2b3be0
Simplify check inside setup function
imciner2 38b984f
Simplify warm start routine
imciner2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
function build(varargin) | ||
% Matlab MEX makefile for OSQP. | ||
% | ||
% MAKE_OSQP(VARARGIN) is a make file for OSQP solver. It | ||
% builds OSQP and its components from source. | ||
% | ||
% WHAT is the last element of VARARGIN and cell array of strings, | ||
% with the following options: | ||
% | ||
% {}, '' (empty string) or 'all': build all components and link. | ||
% | ||
% 'osqp_mex': builds the OSQP mex interface and the OSQP library | ||
% | ||
% Additional commands: | ||
% | ||
% 'clean': Delete all compiled files | ||
% 'purge': Delete all compiled files and copied code generation files | ||
|
||
if( nargin == 0 ) | ||
what = {'all'}; | ||
verbose = false; | ||
elseif ( nargin == 1 && ismember('-verbose', varargin) ) | ||
what = {'all'}; | ||
verbose = true; | ||
else | ||
what = varargin{nargin}; | ||
if(isempty(strfind(what, 'all')) && ... | ||
isempty(strfind(what, 'osqp_mex')) && ... | ||
isempty(strfind(what, 'clean')) && ... | ||
isempty(strfind(what, 'purge'))) | ||
fprintf('No rule to make target "%s", exiting.\n', what); | ||
end | ||
|
||
verbose = ismember('-verbose', varargin); | ||
end | ||
|
||
%% Determine where the various files are all located | ||
% Various parts of the build system | ||
[osqp_classpath,~,~] = fileparts( mfilename( 'fullpath' ) ); | ||
osqp_mex_src_dir = fullfile( osqp_classpath, '..', 'c_sources' ); | ||
osqp_mex_build_dir = fullfile( osqp_mex_src_dir, 'build' ); | ||
osqp_cg_src_dir = fullfile( osqp_mex_build_dir, 'codegen_src' ); | ||
osqp_cg_dest_dir = fullfile( osqp_classpath, '..', 'codegen', 'sources' ); | ||
|
||
% Determine where CMake should look for MATLAB | ||
Matlab_ROOT = strrep( matlabroot, '\', '/' ); | ||
|
||
%% Try to unlock any pre-existing version of osqp_mex | ||
% this prevents compile errors if a user builds, runs osqp | ||
% and then tries to recompile | ||
if(mislocked('osqp_mex')) | ||
munlock('osqp_mex'); | ||
end | ||
|
||
%% Configure, build and install the OSQP mex interface | ||
if( any(strcmpi(what,'osqp_mex')) || any(strcmpi(what,'all')) ) | ||
fprintf('Compiling OSQP solver mex interface...\n'); | ||
|
||
% Create build for the mex file and go inside | ||
if exist( osqp_mex_build_dir, 'dir' ) | ||
rmdir( osqp_mex_build_dir, 's' ); | ||
end | ||
mkdir( osqp_mex_build_dir ); | ||
% cd( osqp_mex_build_dir ); | ||
|
||
% Extend path for CMake mac (via Homebrew) | ||
PATH = getenv('PATH'); | ||
if( (ismac) && (isempty(strfind(PATH, '/usr/local/bin'))) ) | ||
setenv('PATH', [PATH ':/usr/local/bin']); | ||
end | ||
|
||
|
||
|
||
%% Configure CMake for the mex interface | ||
fprintf(' Configuring...' ) | ||
[status, output] = system( sprintf( 'cmake -B %s -S %s -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMatlab_ROOT_DIR=\"%s\"', osqp_mex_build_dir, osqp_mex_src_dir, Matlab_ROOT ), 'LD_LIBRARY_PATH', '' ); | ||
if( status ) | ||
fprintf( '\n' ); | ||
disp( output ); | ||
error( 'Error configuring CMake environment' ); | ||
elseif( verbose ) | ||
fprintf( '\n' ); | ||
disp( output ); | ||
else | ||
fprintf( '\t\t\t\t\t[done]\n' ); | ||
end | ||
|
||
%% Build the mex interface | ||
fprintf( ' Building...') | ||
[status, output] = system( sprintf( 'cmake --build %s --config Release', osqp_mex_build_dir ), 'LD_LIBRARY_PATH', '' ); | ||
if( status ) | ||
fprintf( '\n' ); | ||
disp( output ); | ||
error( 'Error compiling OSQP mex interface' ); | ||
elseif( verbose ) | ||
fprintf( '\n' ); | ||
disp( output ); | ||
else | ||
fprintf( '\t\t\t\t\t\t[done]\n' ); | ||
end | ||
|
||
|
||
%% Install various files | ||
fprintf( ' Installing...' ) | ||
|
||
% Copy mex file to root directory for use | ||
if( ispc ) | ||
[err, errmsg, ~] = copyfile( [osqp_mex_build_dir, filesep, 'Release', filesep, 'osqp_mex.mex*'], [osqp_classpath, filesep, 'private'] ); | ||
else | ||
[err, errmsg, ~] = copyfile( [osqp_mex_build_dir, filesep, 'osqp_mex.mex*'], [osqp_classpath, filesep, 'private'] ); | ||
end | ||
if( ~err ) | ||
fprintf( '\n' ) | ||
disp( errmsg ) | ||
error( ' Error copying mex file' ) | ||
end | ||
|
||
% Copy the code generation source files | ||
% Create build for the mex file and go inside | ||
if exist( osqp_cg_dest_dir, 'dir' ) | ||
rmdir( osqp_cg_dest_dir, 's' ); | ||
end | ||
mkdir( osqp_cg_dest_dir ); | ||
|
||
[err, errmsg, ~] = copyfile( [osqp_cg_src_dir, filesep, '*'], osqp_cg_dest_dir ); | ||
if( ~err ) | ||
fprintf( '\n' ) | ||
disp( errmsg ) | ||
error( ' Error copying code generation source files' ) | ||
end | ||
|
||
fprintf( '\t\t\t\t\t\t[done]\n' ); | ||
end | ||
|
||
%% Clean and purge | ||
if( any(strcmpi(what,'clean')) || any(strcmpi(what,'purge')) ) | ||
fprintf('Cleaning OSQP mex files and build directory...'); | ||
|
||
% Delete mex file | ||
mexfiles = dir(['*.', mexext]); | ||
for i = 1 : length(mexfiles) | ||
delete(mexfiles(i).name); | ||
end | ||
|
||
% Delete OSQP build directory | ||
if exist(osqp_mex_build_dir, 'dir') | ||
rmdir(osqp_mex_build_dir, 's'); | ||
end | ||
|
||
fprintf('\t\t[done]\n'); | ||
|
||
%% Purge only | ||
if( any(strcmpi(what,'purge')) ) | ||
fprintf('Cleaning OSQP codegen directories...'); | ||
|
||
% Delete codegen files | ||
if exist(osqp_cg_dest_dir, 'dir') | ||
rmdir(osqp_cg_dest_dir, 's'); | ||
end | ||
|
||
fprintf('\t\t\t[done]\n'); | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
%% | ||
function codegen(this, target_dir, varargin) | ||
% CODEGEN generate C code for the parametric problem | ||
% | ||
% codegen(target_dir,options) | ||
|
||
% Parse input arguments | ||
p = inputParser; | ||
defaultProject = ''; | ||
expectedProject = {'', 'Makefile', 'MinGW Makefiles', 'Unix Makefiles', 'CodeBlocks', 'Xcode'}; | ||
defaultParams = 'vectors'; | ||
expectedParams = {'vectors', 'matrices'}; | ||
defaultMexname = 'emosqp'; | ||
defaultFloat = false; | ||
defaultLong = true; | ||
defaultFW = false; | ||
|
||
addRequired(p, 'target_dir', @isstr); | ||
addParameter(p, 'project_type', defaultProject, ... | ||
@(x) ischar(validatestring(x, expectedProject))); | ||
addParameter(p, 'parameters', defaultParams, ... | ||
@(x) ischar(validatestring(x, expectedParams))); | ||
addParameter(p, 'mexname', defaultMexname, @isstr); | ||
addParameter(p, 'FLOAT', defaultFloat, @islogical); | ||
addParameter(p, 'LONG', defaultLong, @islogical); | ||
addParameter(p, 'force_rewrite', defaultFW, @islogical); | ||
|
||
parse(p, target_dir, varargin{:}); | ||
|
||
% Set internal variables | ||
if strcmp(p.Results.parameters, 'vectors') | ||
embedded = 1; | ||
else | ||
embedded = 2; | ||
end | ||
if p.Results.FLOAT | ||
float_flag = 'ON'; | ||
else | ||
float_flag = 'OFF'; | ||
end | ||
if p.Results.LONG | ||
long_flag = 'ON'; | ||
else | ||
long_flag = 'OFF'; | ||
end | ||
if strcmp(p.Results.project_type, 'Makefile') | ||
if (ispc) | ||
project_type = 'MinGW Makefiles'; % Windows | ||
elseif (ismac || isunix) | ||
project_type = 'Unix Makefiles'; % Unix | ||
end | ||
else | ||
project_type = p.Results.project_type; | ||
end | ||
|
||
% Check whether the specified directory already exists | ||
if exist(target_dir, 'dir') | ||
if p.Results.force_rewrite | ||
rmdir(target_dir, 's'); | ||
else | ||
while(1) | ||
prompt = sprintf('Directory "%s" already exists. Do you want to replace it? y/n [y]: ', target_dir); | ||
str = input(prompt, 's'); | ||
|
||
if any(strcmpi(str, {'','y'})) | ||
rmdir(target_dir, 's'); | ||
break; | ||
elseif strcmpi(str, 'n') | ||
return; | ||
end | ||
end | ||
end | ||
end | ||
|
||
% Import OSQP path | ||
[osqp_path,~,~] = fileparts(which('osqp.m')); | ||
|
||
% Add codegen directory to path | ||
addpath(fullfile(osqp_path, 'codegen')); | ||
|
||
% Path of osqp module | ||
cg_dir = fullfile(osqp_path, 'codegen'); | ||
files_to_generate_path = fullfile(cg_dir, 'files_to_generate'); | ||
|
||
% Get workspace structure | ||
work = osqp_mex('get_workspace', this.objectHandle); | ||
|
||
% Make target directory | ||
fprintf('Creating target directories...\t\t\t\t\t'); | ||
target_configure_dir = fullfile(target_dir, 'configure'); | ||
target_include_dir = fullfile(target_dir, 'include'); | ||
target_src_dir = fullfile(target_dir, 'src'); | ||
|
||
if ~exist(target_dir, 'dir') | ||
mkdir(target_dir); | ||
end | ||
if ~exist(target_configure_dir, 'dir') | ||
mkdir(target_configure_dir); | ||
end | ||
if ~exist(target_include_dir, 'dir') | ||
mkdir(target_include_dir); | ||
end | ||
if ~exist(target_src_dir, 'dir') | ||
mkdir(fullfile(target_src_dir, 'osqp')); | ||
end | ||
fprintf('[done]\n'); | ||
|
||
% Copy source files to target directory | ||
fprintf('Copying OSQP source files...\t\t\t\t\t'); | ||
cdir = fullfile(cg_dir, 'sources', 'src'); | ||
cfiles = dir(fullfile(cdir, '*.c')); | ||
for i = 1 : length(cfiles) | ||
if embedded == 1 | ||
% Do not copy kkt.c if embedded is 1 | ||
if ~strcmp(cfiles(i).name, 'kkt.c') | ||
copyfile(fullfile(cdir, cfiles(i).name), ... | ||
fullfile(target_src_dir, 'osqp', cfiles(i).name)); | ||
end | ||
else | ||
copyfile(fullfile(cdir, cfiles(i).name), ... | ||
fullfile(target_src_dir, 'osqp', cfiles(i).name)); | ||
end | ||
end | ||
configure_dir = fullfile(cg_dir, 'sources', 'configure'); | ||
configure_files = dir(fullfile(configure_dir, '*.h.in')); | ||
for i = 1 : length(configure_files) | ||
copyfile(fullfile(configure_dir, configure_files(i).name), ... | ||
fullfile(target_configure_dir, configure_files(i).name)); | ||
end | ||
hdir = fullfile(cg_dir, 'sources', 'include'); | ||
hfiles = dir(fullfile(hdir, '*.h')); | ||
for i = 1 : length(hfiles) | ||
if embedded == 1 | ||
% Do not copy kkt.h if embedded is 1 | ||
if ~strcmp(hfiles(i).name, 'kkt.h') | ||
copyfile(fullfile(hdir, hfiles(i).name), ... | ||
fullfile(target_include_dir, hfiles(i).name)); | ||
end | ||
else | ||
copyfile(fullfile(hdir, hfiles(i).name), ... | ||
fullfile(target_include_dir, hfiles(i).name)); | ||
end | ||
end | ||
|
||
% Copy cmake files | ||
copyfile(fullfile(cdir, 'CMakeLists.txt'), ... | ||
fullfile(target_src_dir, 'osqp', 'CMakeLists.txt')); | ||
copyfile(fullfile(hdir, 'CMakeLists.txt'), ... | ||
fullfile(target_include_dir, 'CMakeLists.txt')); | ||
fprintf('[done]\n'); | ||
|
||
% Copy example.c | ||
copyfile(fullfile(files_to_generate_path, 'example.c'), target_src_dir); | ||
|
||
% Render CMakeLists.txt | ||
fidi = fopen(fullfile(files_to_generate_path, 'CMakeLists.txt'),'r'); | ||
fido = fopen(fullfile(target_dir, 'CMakeLists.txt'),'w'); | ||
while ~feof(fidi) | ||
l = fgetl(fidi); % read line | ||
% Replace EMBEDDED_FLAG in CMakeLists.txt by a numerical value | ||
newl = strrep(l, 'EMBEDDED_FLAG', num2str(embedded)); | ||
fprintf(fido, '%s\n', newl); | ||
end | ||
fclose(fidi); | ||
fclose(fido); | ||
|
||
% Render workspace.h and workspace.c | ||
work_hfile = fullfile(target_include_dir, 'workspace.h'); | ||
work_cfile = fullfile(target_src_dir, 'osqp', 'workspace.c'); | ||
fprintf('Generating workspace.h/.c...\t\t\t\t\t\t'); | ||
render_workspace(work, work_hfile, work_cfile, embedded); | ||
fprintf('[done]\n'); | ||
|
||
% Create project | ||
if ~isempty(project_type) | ||
|
||
% Extend path for CMake mac (via Homebrew) | ||
PATH = getenv('PATH'); | ||
if ((ismac) && (isempty(strfind(PATH, '/usr/local/bin')))) | ||
setenv('PATH', [PATH ':/usr/local/bin']); | ||
end | ||
|
||
fprintf('Creating project...\t\t\t\t\t\t\t\t'); | ||
orig_dir = pwd; | ||
cd(target_dir); | ||
mkdir('build') | ||
cd('build'); | ||
cmd = sprintf('cmake -G "%s" ..', project_type); | ||
[status, output] = system(cmd); | ||
if(status) | ||
fprintf('\n'); | ||
fprintf(output); | ||
error('Error configuring CMake environment'); | ||
else | ||
fprintf('[done]\n'); | ||
end | ||
cd(orig_dir); | ||
end | ||
|
||
% Make mex interface to the generated code | ||
mex_cfile = fullfile(files_to_generate_path, 'emosqp_mex.c'); | ||
make_emosqp(target_dir, mex_cfile, embedded, float_flag, long_flag); | ||
|
||
% Rename the mex file | ||
old_mexfile = ['emosqp_mex.', mexext]; | ||
new_mexfile = [p.Results.mexname, '.', mexext]; | ||
movefile(old_mexfile, new_mexfile); | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use Matlab enumeration instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire file needs to be rewritten to support the newer codegen paradigm anyway, so I didn't actually change anything in there. We should do this when we update that file though.