diff --git a/.gitignore b/.gitignore index f25a9c0114f653c281cd0517b259fba4de89fbde..22b4eccd162dfe114956c248d57ba52f9ef908f3 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,7 @@ Thumbs.db # Ignore CMake cache and build directiories (assume build*/) CmakeCache.txt -build*/ +/build*/ **/CMakeFiles # Ignore pycache folder diff --git a/CMakeLists.txt b/CMakeLists.txt index d2b1d7c649ef821b9c414a9ac9141fae07af55a9..b21b2c5954d325cb0c0d95841b2e3f07762d1f57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ set(HYDRA_MODULE_NAME "HIP") find_package(CUDA REQUIRED) find_package(OpenMP REQUIRED) -find_package(Matlab) +find_package(Matlab COMPONENTS MAIN_PROGRAM) find_package(Python COMPONENTS Development NumPy) # Setup backend Hydra CUDA library (static) for cuda building diff --git a/src/MATLAB/+HIP/BuildScript.m b/src/MATLAB/+HIP/BuildScript.m deleted file mode 100644 index 3cb91f573345d303756ccc1fd4426826d8fc1bee..0000000000000000000000000000000000000000 --- a/src/MATLAB/+HIP/BuildScript.m +++ /dev/null @@ -1,141 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Set List of Files to Exclude % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -clear mex -excludeList = ... - {'Cuda.m'; - 'DeviceCount.m'; - 'DeviceStats.m'}; -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% remember curent path -curPath = pwd(); - -% find where the image processing package is -cudaPath = fileparts(which('HIP.BuildMexObject')); -cd(cudaPath) - -% create the m files that correspond to the commands in the mex interface -HIP.BuildMexObject(fullfile('..','..','c',['Mex.' mexext]),'Cuda','HIP'); - -packagePath = cudaPath; -cudaPath = fullfile(cudaPath,'@Cuda'); - -% get a list of all of the functions in the class -dList = dir(fullfile(cudaPath,'*.m')); - -% wrap each function -numFunctions = 0; -for i=1:length(dList) - if (any(strcmpi(excludeList,dList(i).name))) - continue; - end - - % copy the file from the class to the package - newFile = fullfile(packagePath,dList(i).name); -% if (exist(newFile,'file') && ~strcmpi(dList(i).name,'Help.m') && ~strcmpi(dList(i).name,'Info.m')) -% % fprintf(1,'File exist: %s\n',newFile); -% continue; -% end -% fprintf(1,'Making undetected file: %s\n',newFile); - - % get all of the lines - f = fopen(fullfile(cudaPath,dList(i).name),'rt'); - curLine = fgetl(f); - textLines = {}; - while ischar(curLine) - textLines = [textLines;{curLine}]; - curLine = fgetl(f); - end - fclose(f); - - % write out the new data - f = fopen(newFile,'wt'); - funcCallFilled = ''; - for j=1:length(textLines) - curLine = textLines{j}; - - % check for comment line - if (strcmpi(curLine(1),'%')) - %check if it is the protype line - protoLineExpr = '\.Cuda\.'; - protoIdx = regexpi(curLine,protoLineExpr); - if (~isempty(protoIdx)) - % remove the class part of the path - fprintf(f, '%s\n',curLine([1:protoIdx,protoIdx+6:end])); - continue; - end - % doesn't me other searches write as is - fprintf(f,'%s\n',curLine); - continue; - end - - % figure out if this is the function line - funcLineExpr = 'function (?<out>.*) = (?<name>\w+)\((?<param>.*)\)'; - funcCall = regexpi(curLine,funcLineExpr,'names'); - if (~isempty(funcCall)) - funcCallFilled = funcCall; - fprintf(f,'\nfunction %s = %s(%s)\n',funcCall.out,funcCall.name,funcCall.param); - fprintf(f,' try\n'); - fprintf(f,' %s = HIP.Cuda.%s(%s);\n',funcCall.out,funcCall.name,funcCall.param); - fprintf(f,' catch errMsg\n'); - localFuctionCall = sprintf('%s = HIP.Local.%s(%s)',funcCall.out,funcCall.name,funcCall.param); - fprintf(f,' warning(errMsg.message);\n'); - fprintf(f,' %s;\n', localFuctionCall); - fprintf(f,' end\n'); - continue; - end - - mexCallExpr = '\.Mex'; - mexPos = regexpi(curLine,mexCallExpr); - if (~isempty(mexPos)) - % this was written above in the Cuda version of the function - continue - end - - % does not meet any criteria, write as is - fprintf(f, '%s\n',curLine); - end - fclose(f); - - numFunctions = numFunctions +1; - - %place the local function call stub here - if (isempty(funcCallFilled)) - continue - end - - if (~exist(fullfile(packagePath,'+Local'),'dir')) - mkdir(fullfile(packagePath,'+Local')); - end - - localFuncFileName = fullfile(packagePath,'+Local',[funcCallFilled.name,'.m']); - if (~exist(localFuncFileName,'file')) - f = fopen(localFuncFileName,'wt'); - fprintf(f, 'function %s = %s(%s,suppressWarning)\n',funcCallFilled.out,funcCallFilled.name,funcCallFilled.param); - fprintf(f, ' error(''%s not yet implemented in MATLAB!''); %%delete this line when implemented\n',funcCallFilled.name); - fprintf(f, ' if (~exist(''suppressWarning'',''var'') || isempty(suppressWarning) || ~suppressWarning)\n'); - fprintf(f, ' warning(''Falling back to matlab.'');\n'); - fprintf(f, ' end\n'); - fprintf(f, ' \n'); - fprintf(f, ' if (~exist(''numIterations'',''var'') || isempty(numIterations))\n'); - fprintf(f, ' numIterations = 1;\n'); - fprintf(f, ' end\n'); - fprintf(f, ' \n'); - fprintf(f, ' arrayOut = arrayIn;\n'); - fprintf(f, ' for t=1:size(arrayIn,5)\n'); - fprintf(f, ' for c=1:size(arrayIn,4)\n'); - fprintf(f, ' for i=1:numIterations\n'); - fprintf(f, ' %% implement this function here\n'); - fprintf(f, ' arrayOut(:,:,:,c,t) = arrayIn(:,:,:,c,t);\n'); - fprintf(f, ' end\n'); - fprintf(f, ' end\n'); - fprintf(f, ' end\n'); - fprintf(f, 'end\n'); - fclose(f); - end -end - -fprintf('HIP BuildScript wrote %d functions\n',numFunctions); -% go back to the original directory -cd(curPath) diff --git a/src/MATLAB/+HIP/BuildMexObject.m b/src/MATLAB/build-scripts/BuildMexClass.m similarity index 64% rename from src/MATLAB/+HIP/BuildMexObject.m rename to src/MATLAB/build-scripts/BuildMexClass.m index 214bc94d68108de6ef68923256609bfcd5e04e60..d336eb5751e177fe32ed0a596734f395eb60dfd7 100644 --- a/src/MATLAB/+HIP/BuildMexObject.m +++ b/src/MATLAB/build-scripts/BuildMexClass.m @@ -1,4 +1,4 @@ -function BuildMexObject(mexFile, objectName, parentPackage) +function BuildMexClass(mexFile, packagePath, className, parentPackage) oldPath = pwd(); cleanupObj = onCleanup(@()(cleanupFunc(oldPath))); @@ -6,49 +6,48 @@ function BuildMexObject(mexFile, objectName, parentPackage) cd(mexPath); mexFunc = str2func(mexName); - commandList = mexFunc('Info'); + commandList = mexFunc('Info'); - cd(oldPath); + cd(packagePath); % Delete old function definitions - if ( exist(['@' objectName], 'dir') ) - delete(fullfile(['@' objectName], '*.m')); + if ( exist(['@' className], 'dir') ) + delete(fullfile(['@' className], '*.m')); end - makeClassdef(objectName, mexName, commandList); + makeClassdef(className, mexName, commandList); for i=1:length(commandList) - makeStaticMethod(objectName, mexName, commandList(i), parentPackage); + makeStaticMethod(className, mexName, commandList(i), parentPackage); end - copyfile(mexFile, ['@' objectName]); - clear mex + copyfile(mexFile, ['@' className]); end -function makeClassdef(objectName, mexName, commandList) - if ( ~exist(['@' objectName],'dir') ) - mkdir(['@' objectName]); +function makeClassdef(className, mexName, commandList) + if ( ~exist(['@' className],'dir') ) + mkdir(['@' className]); end - objFile = fopen(fullfile(['@' objectName],[objectName '.m']), 'wt'); + classFile = fopen(fullfile(['@' className],[className '.m']), 'wt'); - fprintf(objFile, 'classdef (Abstract,Sealed) %s\n', objectName); + fprintf(classFile, 'classdef (Abstract,Sealed) %s\n', className); - fprintf(objFile, 'methods (Static)\n'); + fprintf(classFile, 'methods (Static)\n'); for i=1:length(commandList) - fprintf(objFile, ' %s\n', makePrototypeString(commandList(i))); + fprintf(classFile, ' %s\n', makePrototypeString(commandList(i))); end - fprintf(objFile, 'end\n'); + fprintf(classFile, 'end\n'); - fprintf(objFile, 'methods (Static, Access = private)\n'); - fprintf(objFile, ' varargout = %s(command, varargin)\n', mexName); - fprintf(objFile, 'end\n'); + fprintf(classFile, 'methods (Static, Access = private)\n'); + fprintf(classFile, ' varargout = %s(command, varargin)\n', mexName); + fprintf(classFile, 'end\n'); - fprintf(objFile, 'end\n'); + fprintf(classFile, 'end\n'); - fclose(objFile); + fclose(classFile); end -function makeStaticMethod(objectName, mexName, commandInfo, parentPackage) - methodFile = fopen(fullfile(['@' objectName],[commandInfo.command '.m']), 'wt'); +function makeStaticMethod(className, mexName, commandInfo, parentPackage) + methodFile = fopen(fullfile(['@' className],[commandInfo.command '.m']), 'wt'); helpLines = strsplit(commandInfo.help, '\n', 'CollapseDelimiters',false); validIdx = find(cellfun(@(x)(~isempty(x)), helpLines)); @@ -71,7 +70,7 @@ function makeStaticMethod(objectName, mexName, commandInfo, parentPackage) fprintf(methodFile, '%% %s - %s\n', commandInfo.command, summaryString); % Write call protoype string - fprintf(methodFile, '%% %s\n', makePrototypeString(commandInfo,objectName,parentPackage,true)); + fprintf(methodFile, '%% %s\n', makePrototypeString(commandInfo,className,parentPackage,true)); % Write remaining help lines directly if ( length(validIdx) > 2 ) @@ -82,13 +81,13 @@ function makeStaticMethod(objectName, mexName, commandInfo, parentPackage) % Output function body fprintf(methodFile, 'function %s\n', makePrototypeString(commandInfo)); - fprintf(methodFile, ' %s;\n', makeCommandString(objectName, mexName,commandInfo,parentPackage)); + fprintf(methodFile, ' %s;\n', makeCommandString(className, mexName,commandInfo,parentPackage)); fprintf(methodFile, 'end\n'); fclose(methodFile); end -function commandString = makeCommandString(objectName, mexName, commandInfo, parentPackage) +function commandString = makeCommandString(className, mexName, commandInfo, parentPackage) commandString = ''; if ( ~isempty(commandInfo.outArgs) ) commandString = ['[' makeCommaList(commandInfo.outArgs) '] = ']; @@ -100,7 +99,7 @@ function commandString = makeCommandString(objectName, mexName, commandInfo, par parentPackage = [parentPackage '.']; end - mexCall = [parentPackage objectName '.' mexName]; + mexCall = [parentPackage className '.' mexName]; commandString = [commandString mexCall '(''' commandInfo.command '''']; if ( ~isempty(commandInfo.inArgs) ) commandString = [commandString ',' makeCommaList(commandInfo.inArgs)]; @@ -108,9 +107,9 @@ function commandString = makeCommandString(objectName, mexName, commandInfo, par commandString = [commandString ')']; end -function protoString = makePrototypeString(commandInfo, objectName, parentPackage, leaveOptBrackets) - if ( ~exist('objectName','var') ) - objectName = []; +function protoString = makePrototypeString(commandInfo, className, parentPackage, leaveOptBrackets) + if ( ~exist('className','var') ) + className = []; end if ( ~exist('parentPackage','var') ) parentPackage = []; @@ -131,8 +130,8 @@ function protoString = makePrototypeString(commandInfo, objectName, parentPackag protoString = [protoString ' = ']; end - if ( ~isempty(objectName) ) - protoString = [protoString parentPackage objectName '.']; + if ( ~isempty(className) ) + protoString = [protoString parentPackage className '.']; end protoString = [protoString commandInfo.command '(']; @@ -156,5 +155,4 @@ end function cleanupFunc(oldPath) cd(oldPath); - clear mex; end diff --git a/src/MATLAB/build-scripts/autoInstallMex.m b/src/MATLAB/build-scripts/autoInstallMex.m new file mode 100644 index 0000000000000000000000000000000000000000..80433c19f74ac1a62235a5b2d92ab8f80d702bfa --- /dev/null +++ b/src/MATLAB/build-scripts/autoInstallMex.m @@ -0,0 +1,74 @@ +function autoInstallMex(moduleName, mexFile) + className = 'Cuda'; + + hipDir = fullfile(pwd(),'..',['+' moduleName]); + if ( ~exist(hipDir,'dir') ) + mkdir(hipDir); + end + + BuildMexClass(mexFile, hipDir, className, moduleName); + wrapClassFuncs(hipDir, className); +end + +function wrapClassFuncs(packageDir, className) + classDir = fullfile(packageDir, ['@' className]); + + % Exclude internal/cuda-specific functions + excludeList = {[className '.m']; + 'DeviceCount.m'; + 'DeviceStats.m'}; + + % get a list of all of the functions in the class + funcList = dir(fullfile(classDir,'*.m')); + + for i=1:length(funcList) + if (any(strcmpi(excludeList, funcList(i).name))) + continue; + end + + wrapFile = fullfile(packageDir,funcList(i).name); + inFile = fullfile(classDir,funcList(i).name); + + inData = fileread(inFile); + inLines = strsplit(inData, {'\n','\r\n'}); + + wrapLines = updateLines(inLines, className); + wrapStr = strjoin(wrapLines, '\n'); + fid = fopen(wrapFile, 'wt'); + fprintf(fid,'%s', wrapStr); + fclose(fid); + end +end + +function outLines = updateLines(inLines, className) + outLines = {}; + for i=1:length(inLines) + chkLine = inLines{i}; + + classExpr = regexptranslate('escape', ['.' className '.']); + commentProtoExpr = ['(%.*?)' classExpr]; + funcLineExpr = 'function (?<out>.*) = (?<name>\w+)\((?<params>.*)\)'; + + commentProto = regexpi(chkLine, commentProtoExpr, 'once'); + funcLine = regexpi(chkLine, funcLineExpr, 'once'); + if ( ~isempty(commentProto) ) + outLines = [outLines; {regexprep(chkLine, commentProtoExpr, '$1.')}]; + elseif ( ~isempty(funcLine) ) + funcCall = regexpi(chkLine, funcLineExpr, 'names'); + + outLines = [outLines; {sprintf('function %s = %s(%s)', funcCall.out, funcCall.name, funcCall.params)}]; + outLines = [outLines; {sprintf(' try')}]; + outLines = [outLines; {sprintf(' %s = HIP.Cuda.%s(%s);',funcCall.out,funcCall.name,funcCall.params)}]; + outLines = [outLines; {sprintf(' catch errMsg')}]; + outLines = [outLines; {sprintf(' warning(errMsg.message);')}]; + outLines = [outLines; {sprintf(' %s = HIP.Local.%s(%s);',funcCall.out,funcCall.name,funcCall.params)}]; + outLines = [outLines; {sprintf(' end')}]; + outLines = [outLines; {sprintf('end')}]; + outLines = [outLines; {''}]; + + break; + else + outLines = [outLines; {chkLine}]; + end + end +end diff --git a/src/c/Mex/autoBuildMex.cmake b/src/c/Mex/autoBuildMex.cmake new file mode 100644 index 0000000000000000000000000000000000000000..eaec3ac7cbe1ed4ebbce0b6c95962a2ca25c1a15 --- /dev/null +++ b/src/c/Mex/autoBuildMex.cmake @@ -0,0 +1,6 @@ +add_custom_command(TARGET HydraMex + POST_BUILD + COMMAND ${Matlab_MAIN_PROGRAM} -nosplash -nodisplay -nodesktop $<$<PLATFORM_ID:Windows>:-wait> -r "autoInstallMex('${HYDRA_MODULE_NAME}', '$<TARGET_FILE:HydraMex>');exit;" + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/MATLAB/build-scripts + VERBATIM USES_TERMINAL +)