classdef ToolchainInfo < handle
    %ToolchainInfo TOOLCHAININFO Class representation of a toolchain's specifications.

    %   Copyright 2012-2020 The MathWorks, Inc.
    
    properties
        Name = ''
        BuildTools
        PrebuildTools
        PostbuildTools
        BuilderApplication
        BuildConfigurations
        Macros
        MATLABSetup = []
        MATLABCleanup = []
        ShellSetup = []
        ShellCleanup = []
        InlinedCommands = []
        Revision = '1.0'
        %Platform Platform -  The supported host platform, e.g. 64-bit Windows
        %
        Platform = 'win64'
        %SupportedVersion SupportedVersion -  The supported version of the toolchain
        %
        SupportedVersion = ''
        SupportsBuildingMEXFuncs = false
        Attributes
    end
    
    properties (SetAccess=protected)
        SupportedLanguages = { 'C/C++' }
        BuildArtifact
        BuildArtifactType
        BuildArtifactWriter
        BuildArtifactExecutor
        FormatVersion
    end
    
    properties (Hidden)
        Alias = { '' }
        CustomBuildConfigurations
    end
    
    properties (Access=protected,Hidden)
        ReservedNames
    end
    
    methods (Static)
        obj = loadobj(inObj)
        default = getBaselineBuildConfigs
        out = getSupportedLanguages(filter)
        default = getDefaultBuildConfig
        out = removeEmptyCellElements(in)
    end
    
    methods
        function h = ToolchainInfo(varargin)
            [varargin{:}] = convertStringsToChars(varargin{:});

            if nargin==1 && isa( varargin{ 1 }, 'coder.make.ToolchainInfo' )
                h.clone( varargin{ 1 } );
            else
                h.initialize( varargin{ : } );
            end
        end
        function set.Name(h,value)
            value = convertStringsToChars(value);

            if ~ischar( value )
                error( message( 'coder_compile:toolchain:InvalidNameFormat' ) );
            end
            h.Name = value;
        end
        function set.Alias(h,value)
            value = convertStringsToChars(value);

            if ~iscellstr( value )
                error( message( 'coder_compile:toolchain:InvalidAliasFormat' ) );
            end
            h.Alias = value;
        end
        function set.SupportedLanguages(h,value)
            value = convertStringsToChars(value);

            if isempty( value )
                value = { 'C/C++' };
            elseif ischar( value )
                value = cellstr( value );
            elseif ~iscellstr( value )
                error( message( 'coder_compile:toolchain:InvalidLanguageFormat' ) );
            end
            nl = newline;
            str = [ '-----------    ----------   ----------   ---------- ', nl, ' Supported      Supports     Supports     Supports  ', nl, ' Language       Assembly        C           C++     ', nl, '-----------    ----------   ----------   ---------- ', nl, ' C                             yes                  ', nl, ' C++                           yes          yes     ', nl, ' C/C++                         yes          yes     ', nl, ' Asm/C           yes           yes                  ', nl, ' Asm/C++         yes           yes          yes     ', nl, ' Asm/C/C++       yes           yes          yes     ', nl, '-----------    ----------   ----------   ---------- ', nl ];
            if numel( value )>1
                error( message( 'coder_compile:toolchain:InvalidLanguageSize', str ) );
            else
                supported = coder.make.ToolchainInfo.getSupportedLanguages();
                if isempty( intersect( supported, value ) )
                    error( message( 'coder_compile:toolchain:InvalidLanguageValue', value{ 1 }, str ) );
                end
                if strcmpi( 'C++', value{ 1 } )
                    value = { 'C/C++' };
                elseif strcmpi( 'Asm/C++', value{ 1 } )
                    value = { 'Asm/C/C++' };
                end
            end
            % Set the value
            h.SupportedLanguages = value;
        end
        function set.Platform(h,value)
            value = convertStringsToChars(value);

            if ~isempty( value )
                validValues = { 'win32', 'glnx86', 'win64', 'glnxa64', 'maci64' };
                if ~ischar( value )
                    error( message( 'coder_compile:toolchain:PropertyMustBeString', 'Platform', class( h ) ) );
                elseif ~any( strcmpi( value, validValues ) )
                    error( message( 'coder_compile:toolchain:InvalidPlatform', '''win32'', ''glnx86'', ''win64'', ''glnxa64'', ''maci64''' ) );
                end
            end
            h.Platform = lower(value);
            % TODO: What is the best way to set the MEX extension?
            h.setMexExtBasedOnPlatform();
        end
        function set.InlinedCommands(h,value)
            value = convertStringsToChars(value);

            if ischar( value )
                if isempty( value )
                    value = [  ];
                else
                    value = cellstr( value );
                end
            end
            if ~isempty( value ) && ~iscell( value )  %% ~iscellstr(value)
                error( message( 'coder_compile:toolchain:PropertyMustBeStringOrCell', 'InlinedCommands', class( h ) ) );
            end
            h.InlinedCommands = value;
        end
        function set.Attributes(h,value)
            datatype = 'coder.make.util.UnorderedList';
            if ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'Attributes', class( h ), datatype ) )
            end
            h.Attributes = value;
        end
        function set.BuilderApplication(h,tool)
            narginchk( 2, 2 );
            if ~h.isBuildTool( tool )
                error( message( 'coder_compile:toolchain:InvalidBuildArtifactToolValue', class( tool ) ) );
            end
            h.BuilderApplication = tool;
        end
        function set.BuildTools(h,value)
            datatype = 'coder.make.util.OrderedList';
            if ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'BuildTools', class( h ), datatype ) )
            end
            h.BuildTools = value;
        end
        function set.BuildConfigurations(h,value)
            datatype = 'coder.make.util.UnorderedList';
            if ~isempty( value ) && ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'BuildConfigurations', class( h ), datatype ) );
            end
            h.BuildConfigurations = value;
        end
        function set.PrebuildTools(h,value)
            datatype = 'coder.make.util.OrderedList';
            if ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'PrebuildTools', class( h ), datatype ) )
            end
            h.PrebuildTools = value;
        end
        function set.PostbuildTools(h,value)
            datatype = 'coder.make.util.OrderedList';
            if ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'PostbuildTools', class( h ), datatype ) )
            end
            h.PostbuildTools = value;
        end
        function set.Macros(h,value)
            datatype = 'coder.make.util.OrderedList';
            if ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'Macros', class( h ), datatype ) )
            end
            h.Macros = value;
        end
        function set.MATLABSetup(h,value)
            narginchk( 2, 2 );
            value = h.checkSetupCleanup( 'MATLABSetup', value );
            value = convertStringsToChars(value);
            h.MATLABSetup = value;
        end
        function set.MATLABCleanup(h,value)
            narginchk( 2, 2 );
            value = h.checkSetupCleanup( 'MATLABCleanup', value );
            value = convertStringsToChars(value);
            h.MATLABCleanup = value;
        end
        function set.ShellSetup(h,value)
            narginchk( 2, 2 );
            value = h.checkSetupCleanup( 'ShellSetup', value );
            value = convertStringsToChars(value);
            h.ShellSetup = value;
        end
        function set.ShellCleanup(h,value)
            narginchk( 2, 2 );
            value = h.checkSetupCleanup( 'ShellCleanup', value );
            value = convertStringsToChars(value);
            h.ShellCleanup = value;
        end
        function set.Revision(h,value)
            value = convertStringsToChars(value);

            if ~isempty( value ) && ~ischar( value )
                error( message( 'coder_compile:toolchain:PropertyMustBeString', 'Revision', class( h ) ) );
            end
            h.Revision = value;
        end
        function set.SupportedVersion(h,value)
            if nargin > 1
                value = convertStringsToChars(value);
            end

            if ~isempty( value ) && ~ischar( value )
                error( message( 'coder_compile:toolchain:PropertyMustBeString', 'SupportedVersion', class( h ) ) );
            end
            h.SupportedVersion = value;
        end
        function set.BuildArtifact(h,value)
            value = convertStringsToChars(value);
            
            if ~isempty( value ) && ~ischar( value )
                error( message( 'coder_compile:toolchain:PropertyMustBeString', 'BuildArtifact', class( h ) ) );
            end
            h.BuildArtifact = value;
        end
        function set.BuildArtifactType(h,value)
            datatype = 'coder.make.enum.BuildArtifact';
            if ~isempty( value ) && ~isa( value, datatype )
                error( message( 'coder_compile:toolchain:PropertyMustBeString', 'BuildArtifactType', class( h ) ) );
            end
            h.BuildArtifactType = value;
        end
        function set.BuildArtifactWriter(h,value)
            value = convertStringsToChars(value);

            if ~isempty( value ) && ~ischar( value )
                error( message( 'coder_compile:toolchain:PropertyMustBeString', 'BuildArtifactWriter', class( h ) ) );
            end
            h.BuildArtifactWriter = value;
        end
        function set.BuildArtifactExecutor(h,value)
            value = convertStringsToChars(value);

            if ~isempty( value ) && ~ischar( value )
                error( message( 'coder_compile:toolchain:PropertyMustBeString', 'BuildArtifactExecutor', class( h ) ) );
            end
            h.BuildArtifactExecutor = value;
        end
        function set.SupportsBuildingMEXFuncs(h,value)
            if ~islogical( value )
                error( message( 'coder_compile:toolchain:PropertyMustBeBoolean', 'SupportsBuildingMEXFuncs', class( h ) ) );
            end
            h.SupportsBuildingMEXFuncs = value;
        end
        function set.CustomBuildConfigurations(h,value)
            if ~isempty( value )
                datatype = 'coder.make.util.UnorderedList';
                if ~isa( value, datatype )
                    error( message( 'coder_compile:toolchain:InvalidPropertyValue', 'CustomBuildConfigurations', class( h ), datatype ) )
                end
            end
            h.CustomBuildConfigurations = value;
        end
    end
    
    methods (Hidden)
        setBuildArtifactProperties(h,artifact,writer,executor)
        clone(h,src)
        copyToolchainInfo(h,src)
        out = isEqualTo(h,src)
        resp = getBuildConfigsAndOptions(h,varargin)
        ordered = orderBuildConfigsAndOptions(h,unordered)
        options = getBuildConfigOrderedOptions(h)
        initialize(h,varargin)
        generateRequiredTools(h)
        resp = generatesMakefile(h)
        resp = generatesIDEProject(h)
        resp = isCustomBuildArtifact(h)
        generateBuildConfigurations(h,language)
        generateBuildTools(h,language,buildToolType,buildTool)
        generateBuilderApplication(h,language,platform)
        addBuilderApplication(h,language,toolname)
        addIDETool(h,language,platform)
        tool = createBuildTool(h,name)
        [prebuild,prebuildKey] = createPrebuildTools(h)
        [postbuild,postbuildKey] = createPostbuildTools(h)
        tool = createAndAddBuildTool(h,buildToolType,toolKey,toolInfo,option)
        setBuilderApplication(h,platform)
        out = createBuildItem(h,varargin)
        toolValue = addPrePostBuildTool(h,preOrPost,afterTool,toolName,toolValue,varargin)
        out = getDerivedExtensions(h,strict)
        out = getOptionMacros(h,preference)
        [out,outkeys] = getAllTools(h)
        addCustomBuildConfiguration(h,cfgName,varargin)
        removeCustomBuildConfiguration(h,cfgName)
        bc = getCustomBuildConfiguration(h,cfgName)
        value = getCustomBuildConfigurationOption(h,cfgName,option,action)
        ret = hasCustomBuildConfiguration(h,cfgName)
        cfgNames = getCustomBuildConfigurationNames(h)
        setProjectTool(h,toolClass);
        tool = getProjectTool(h);
    end
    
    methods (Access=private)
        addBuildToolByConstructor(h,varargin)
        varargout = addBuildToolCommon(h,byUser,buildToolType,name,tool)
        addBuildConfigByConstructor(h,cfgName,varargin)
        addBuildConfigCommon(h,byUser,cfgName,cfgDesc)
        [options,macros,values] = getBuildConfigOptionsInfo(h)
        checkBuildConfigOptionForGetSet(h,config,name,value)
        checkBuildToolsForGetSet(h,toolType,name,tool)
        prop = getToolsProp(h,toolType)
        checkBuildToolsForAdd(h,desc,prop,name,tool)
        checkIfValidTool(h,name,tool)
        [success,problem,reportStr] = configureToolchain...
            (h, action, lBuildLogger, runMatlabScriptOnly)
        configureProperty(prop,verbose)
        [success,warnStr,errorStr,reportStr] = privateValidateToolchain(h,setupToolchain,cleanupToolchain)
        out = getInfo(h,itemOrder)
        optsValue = getOptionsString(h,options)
        resp = getReservedNames(h,field)
        addReservedNames(h,field,names)
        initializeBuildConfigOptions(h,tool)
        initializeBuildConfigOption(h,optionName,optionMacro,optionValue)
        varargout = checkBuildConfigFor(h,name)
        errorIfReservedName(h,cfgName)
        resp = getFileExtensionItem(h,aFileExt,returnType)
        resp = getDirectiveItem(h,aDirective,returnType)
        resp = getABuildToolListPropertyEntry(h,prop,key,returnType)
        value = checkSetupCleanup(h,prop,value)
        resp = isBuildTool(h,tool)
        out = createOrderedList(h,varargin)
        out = createUnorderedList(h,varargin)
    end
    
    methods (Hidden)
        reset(h)
        cfgs = getBuildConfigurations(h)
        names = getBuildConfigurationNames(h)
        addBuildConfigurationFromFile(h,filename)
        filenames = saveBuildConfigurations(h)
        saveBuildConfigurationToFile(h,configs,filenames)
        value = getBuildConfigurationOption(h,name,option,action)
        setBuildConfigurationOption(h,cfg,name,value)
        setBuildToolName(h,buildtool,value)
        setBuildToolCommand(h,buildtool,varargin)
        setBuildToolOptions(h,buildtool,value)
        setBuildToolPath(h,buildtool,varargin)
        setBuildToolDirective(h,buildtool,name,value)
        setBuildToolFileExtension(h,buildtool,name,value)
        setBuildToolCommandPattern(h,buildtool,value)
        resp = getFileExtension(h,fileType)
        resp = getDirective(h,fileType,action)
        out = fullfile(h,varargin)
        exts = getFileExtensions(h,buildtool,item)
        out = getProp(h,prop)
        macros = getAllMacros(h)
        macros = getUninitializedMacros(h)
        varargout = checkMacros(h,varargin)
        names = extractIntrinsicMacros(h,names)
        [missing,uninitialized,supportsCpp] = locateCppLinker(h)
        [pass,message,recommendation] = validateSpecialLinkerProperties(h)
        addCppLinker(h)
        addOrUpdateBuildToolDirective(h,directive)
        checkHandleIfValid(h,methodName)
        setMexExtBasedOnPlatform(h)
        list = updateIfUsingAlias(h,list)
    end
end

% [EOF]
