classdef (Hidden) Builder < rtw.connectivity.Component
%BUILDER builds an application
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2007-2020 The MathWorks, Inc.
%   

    properties (Access=private)
        TargetApplicationFramework;     
    end    
    
    properties (SetAccess = 'private', GetAccess = 'protected')
        IsSilAndPortableWordSizes;
        BuildStrategy rtw.connectivity.ConfigBuildStrategy = rtw.connectivity.ConfigBuildStrategy();
    end 
    
    methods
        
        function this = Builder(componentArgs, targetApplicationFramework)
            
            narginchk(2, 2);
        
            % call super class constructor                             
            this@rtw.connectivity.Component(componentArgs);
            
            % validate arg
            rtw.connectivity.Utils.validateArg(...
                targetApplicationFramework, ...
                'rtw.connectivity.TargetApplicationFramework');
            
            % store application framework            
            this.TargetApplicationFramework = targetApplicationFramework;
            
            % cache whether this is a SIL+PWS simulation
            this.IsSilAndPortableWordSizes = this.getComponentArgs.isSILAndPWS;
            
            % Setup the build strategy for rtw.pil.ApplicationBuilder to
            % execute
            defaultBuildOptions = rtw.pil.SILPILBuildOptions();
            defaultBuildOptions.ComponentArgs = componentArgs;
            this.BuildStrategy = rtw.connectivity.ConfigBuildStrategy();
            this.BuildStrategy.setPILBuildOptions(defaultBuildOptions);
            
            % We need to inject the member protected methods which could be
            % overridden. To the build strategy used by
            % rtw.pil.ApplicationBuildManager.
            this.BuildStrategy.registerFunctionOverride('getComponentBuildInfoMATFileContents', @(b) this.getComponentBuildInfoMATFileContents(b));
            this.BuildStrategy.registerFunctionOverride('processComponentIntegrationBuildInfo', @(b) this.processComponentIntegrationBuildInfoStrategyWrapper(b));
            this.BuildStrategy.registerFunctionOverride('createApplicationBuildInfo', @(cB, tAB, pTAB) this.createApplicationBuildInfo(cB, tAB, pTAB));
            this.BuildStrategy.registerFunctionOverride('doBuild', @(b) this.doBuildStrategyWrapper(b));            
            this.BuildStrategy.registerFunctionOverride('doUpdateBuild', @(b) this.doUpdateBuildStrategyWrapper(b));
            this.BuildStrategy.registerFunctionOverride('getApplicationPath', @() this.getApplicationPathStrategyWrapper());
        end
    end   
    
    methods (Hidden)    
       function t = getTargetApplicationFramework(this)
           t = this.TargetApplicationFramework;
       end    
    end

    % methods can't be overridden
    methods (Sealed, Hidden)        
        
        function strategy = getBuildStrategy(this)
            strategy = this.BuildStrategy;
        end
        
        % Get full file name of the executable
        function exe = getApplicationExecutable(this)
            narginchk(1, 1);
            % get the app BuildInfo
            applicationBuildInfoPath = fullfile(this.getComponentArgs.getApplicationCodePath, 'buildInfo.mat');
            if ~exist(applicationBuildInfoPath, 'file')
                rtw.connectivity.ProductInfo.error('target', 'FileNotFound', ...
                      applicationBuildInfoPath);                  
            else
                % load the BuildInfo
                applicationBuildInfo = targets_load_buildinfo(applicationBuildInfoPath);
                % determine executable
                exe = this.getExecutable(applicationBuildInfo);
            end                                                                   
        end
    end    
        
    % Protected methods which could be overridden by target authors in
    % subclasses. By default these will use the default behavior as
    % specified by rtw.pil.ApplicationBuildManager.
    methods (Access = 'protected')  
        
       % updateBuild is issued when applicationBuildInfo has not been
       % modified since the last build. Generated projects / makefiles 
       % should be bought up to date rather than regenerating them if possible. 
       function updateBuild(this, applicationBuildInfo, lXilCompInfo, varargin) %#ok<INUSD>
          
           this.BuildStrategy.getDefaultBehavior().doUpdateBuild(applicationBuildInfo);
       end               
                           
       function contents = getComponentBuildInfoMATFileContents(this, componentBuildInfoPath)
           
           contents = this.BuildStrategy.getDefaultBehavior().getComponentBuildInfoMATFileContents(componentBuildInfoPath);
       end
              
       function [applicationBuildInfo, appFrameworkUpdated] = createApplicationBuildInfo(this, ...
                                                                  componentIntegrationBuildInfo, ...
                                                                  targetApplicationFrameworkBuildInfo, ...
                                                                  prevTargetApplicationFrameworkBuildInfo)
           [applicationBuildInfo, appFrameworkUpdated] = ...
                this.BuildStrategy.DefaultBuildStrategy.createApplicationBuildInfo(componentIntegrationBuildInfo, ...
                                                                                   targetApplicationFrameworkBuildInfo, ...
                                                                                   prevTargetApplicationFrameworkBuildInfo);
       end
    end
        
    % Abstract methods to be implemented by subclasses.
    methods (Abstract, Access = 'protected')
        
        % Get a buildInfo object that represents how to integrate with the
        % component
        componentIntegrationBuildInfo = getComponentIntegrationBuildInfo(this, componentBuildInfo, xilCompInfo)
                
        % Full build of the application. Here applicationBuildInfo has been
        % updated. This should regenerate projects / makefiles bringing
        % them up to date.
        build(this, applicationBuildInfo, xilCompInfo, varargin)
        
        % Get full file name of the executable
        exe = getExecutable(this, applicationBuildInfo)
    end
    
    % BuildStrategy wrapper calls. Allows
    % rtw.pil.ApplicationBuildManager to call overridden methods from the
    % connectivity config by registering these to the build strategy in the
    % constructor
    methods (Sealed, Access=private)        
        
        function buildInfo = processComponentIntegrationBuildInfoStrategyWrapper(this, componentBuildInfo)
            
            buildInfo = this.getComponentIntegrationBuildInfo(componentBuildInfo, this.BuildStrategy.PILBuildOptions.XilCompInfo);            
        end
        
        function doBuildStrategyWrapper(this, buildInfo)
            
            args = this.BuildStrategy.PILBuildOptions.getArgumentsAsNameValuePairs();
            this.build(buildInfo, this.BuildStrategy.PILBuildOptions.XilCompInfo, args{:});
        end
        
        function doUpdateBuildStrategyWrapper(this, buildInfo)
            
            args = this.BuildStrategy.PILBuildOptions.getArgumentsAsNameValuePairs();
            this.updateBuild(buildInfo, this.BuildStrategy.PILBuildOptions.XilCompInfo, args{:});
        end
                
        function applicationPath = getApplicationPathStrategyWrapper(this)            
            
            applicationPath = this.getApplicationExecutable();
        end
    end 
end

