classdef (Hidden=true) Executor < handle
    % This is an abstract class

    %   Copyright 2012-2019 The MathWorks, Inc.
    
    properties (SetAccess = immutable)
        Parent
        BuildOpts
        ToolchainInfo
    end
    
    properties
        BuildData = [];
        BuildArtifacts
        BuildArtifactType
        BuildOutput
        Command
    end
    
    properties(Access=protected)
        ExecutorBuildLogger;
    end
    
    % -----------------------
    % ABSTRACT METHODS
    % -----------------------
    methods (Abstract)
        
        % -----------------------------------------------------------------
        % The following abstract methods are required to have the following
        % inputs and outputs.
        % Inputs:
        %   H         - (handle) object handle
        %   BUILDINFO - (handle) BuildInfo handle
        % Outputs:
        %   STATUS    - (boolean) indicates if the operation passed or failed
        %   PROBLEM   - (string) the problem encountered, if any
        % -----------------------------------------------------------------
        
        % Run the artifact which generates the output binary
        [sucess, problem] = build(h, buildInfo);
        % Run the artifact which generates the output binary
        [sucess, problem] = clean(h, buildInfo);
        % Download the generated binary
        [sucess, problem] = download(h, buildInfo);
        % Run the generated binary
        [sucess, problem] = execute(h, buildInfo);
        
        % -----------------------------------------------------------------
        % Diagnose a build, download, or execute problem.
        % Inputs:
        %   H         - (handle) object handle
        %   PROBLEM   - (string) the problem encountered
        %   STEP      - (string) the step invoked
        % Outputs:
        %   MSG       - (string) message to the user
        % -----------------------------------------------------------------
        msg = diagnose(h, problem, step);
        
    end % abstract methods
    
    % -----------------------
    % GET/SET METHODS
    % -----------------------
    methods
                
        function out = getBuildOpts(h)
            out = h.BuildOpts;
        end
        
        function set.BuildArtifactType(h, value)
            if ~isa(value, 'coder.make.enum.BuildArtifact')
                error(message('CoderFoundation:utility:InvalidProperty', ...
                    'BuildArtifactType', class(h), 'coder.make.enum.BuildArtifact'))
            end
            h.BuildArtifactType = value;
        end

        function set.BuildArtifacts(h, value)
            if ~iscell(value) %% ~iscellstr(value)
                error(message('CoderFoundation:utility:InvalidProperty', ...
                    'BuildArtifacts', class(h), 'cell array of strings'))
            end
            h.BuildArtifacts = value;
        end
        
        function set.BuildOutput(h, value)
            if ~ischar(value)
                error(message('CoderFoundation:utility:InvalidProperty', ...
                    'BuildOutput', class(h), 'string'))
            end
            h.BuildOutput = value;
        end        
        
        function set.Command(h, value)
            if ~isempty(value) && ~isstruct(value)
                error(message('CoderFoundation:utility:InvalidProperty', ...
                    'Command', class(h), 'struct'))
            end
            h.Command = value;
        end
        
    end % get/set methods
    
    % -----------------------
    % PUBLIC METHODS
    % -----------------------
    methods (Access = public)
        
        % -----------------------------------------------------------------
        % Constructor
        % -----------------------------------------------------------------
        function h = Executor(builder, buildInfo, artifact)
            h.Parent         = builder;
            h.BuildArtifacts = artifact;
            h.ToolchainInfo  = h.Parent.ToolchainInfo;
            h.BuildOpts      = buildInfo.getBuildToolInfo('BuildOpts');
            h.ExecutorBuildLogger      = builder.BuilderBuildLogger;
        end
        
        % -----------------------------------------------------------------
        % Run the executor
        % -----------------------------------------------------------------
        function [success, results] = run(h, buildInfo)
            
            results = [];
            toolchainInfo = h.ToolchainInfo;
            % -------------------------------------
            % Setup the toolchain
            % -------------------------------------
            lBuildLogger = h.ExecutorBuildLogger;
            [success, problem] = toolchainInfo.toolchainSetup(lBuildLogger);
            if ~success
                results = h.diagnose('setup', problem);
                return;
            end
            
            % -------------------------------------
            % Run the build artifact
            % -------------------------------------
            
            if h.hasBuildAction(coder.make.enum.BuildAction.BUILD)
                
                [success, problem] = h.build(buildInfo);
                results = h.diagnose('build', problem);
                if ~success                    
                    return;
                end
            end
            
            % -------------------------------------
            % Invoke the download tool
            % -------------------------------------
            if h.hasBuildAction(coder.make.enum.BuildAction.DOWNLOAD)
                
                [success, problem] = h.download(buildInfo);
                if ~success
                    results = h.diagnose('download', problem);
                    return;
                end
            end
            
            % -------------------------------------
            % Invoke the execute tool
            % -------------------------------------
            if h.hasBuildAction(coder.make.enum.BuildAction.EXECUTE)
                
                [success, problem] = h.execute(buildInfo);
                if ~success
                    results = h.diagnose('execute', problem);
                    return;
                end
                
            end
            
            % -------------------------------------
            % Give the executor a chance to cleanup
            % -------------------------------------
            h.cleanup(buildInfo);
            
            % -------------------------------------
            % Cleanup the toolchain
            % -------------------------------------
            lBuildLogger = h.ExecutorBuildLogger;
            [success, problem] = toolchainInfo.toolchainCleanup(lBuildLogger); 
            if ~success
                results = h.diagnose('cleanup', problem);
                return;
            end
            
        end % run()
        
    end % public methods
    
    methods
        
        % -----------------------------------------------------------------
        % (Utility) Does this build contain the specified build action?
        % -----------------------------------------------------------------
        function resp = hasBuildAction(h, buildAction)
            resp = any(h.Parent.BuildActions == buildAction);
        end 
        
        function cleanup(h, buildInfo) %#ok<INUSD>
            %Default action is nothing
        end
    end
    
end

% LocalWords:  BUILDINFO toolchain
