classdef UpdatedLibArchivePoller < handle
% UPDATEDLIBARCHIVEPOLLER polls for existence or updated timestamp of 
% library files that must have been generated; this polling is 
% necessary in some cases to prevent race conditions due to file system
% latencies
    
%   Copyright 2019-2020 The MathWorks, Inc.
    
    properties(Access=public)
        MaxWaitTimeInSeconds = 20;
    end
    
    properties(Access=private)
        FolderContentInitial
        LibPaths
    end
    
    methods(Access=public)
        
        function this = UpdatedLibArchivePoller(lLibsToBuildPaths, lLibsToBuildNames)
        % Initialize the directory listing for each library that might be created
            
            this.FolderContentInitial = cell(size(lLibsToBuildPaths));
            this.LibPaths = lLibsToBuildPaths;
            
            % Capture directory listing (including timestamp) for each library file
            for i=1:length(lLibsToBuildPaths)
                % Get directory listing (with timestamp) for library to be generated
                this.FolderContentInitial{i} = ...
                    i_getDirLibBefore(fullfile(lLibsToBuildPaths{i}, lLibsToBuildNames{i}));
            end
            
        end
        
        
        function pollForUpdatedLibArchives(this, buildResults)
                
            pauseTime = 0.1;
            maxCount = floor(this.MaxWaitTimeInSeconds / pauseTime);
            
            for i=1:length(this.FolderContentInitial)
                
                % Only perform the check if a new library archive was
                % generated
                if ~buildResults(i).FoundCreatedMessage
                    continue;
                end
                
                binaryFileNoPath = buildResults(i).FinalProduct;
                binaryFile = fullfile(this.LibPaths{i}, binaryFileNoPath);
                
                % Now that we know the full name (including extension) of
                % the library we can identify if it existed prior to the
                % build
                dirBinaryFileBefore = this.FolderContentInitial{i};
                candidateLibFilesBefore = {dirBinaryFileBefore.name};
                matchIdx = strcmp(binaryFileNoPath, candidateLibFilesBefore);
                dirBinaryFileBefore = dirBinaryFileBefore(matchIdx);                
                
                dirSharedLib = dir(binaryFile);
                
                if ~isempty(dirBinaryFileBefore)
                    timestampBefore = dirBinaryFileBefore.datenum;
                else
                    timestampBefore = 0;
                end
                
                count = 0;
                while isempty(dirSharedLib) || (dirSharedLib.datenum <= timestampBefore)
                    pause(pauseTime)
                    dirSharedLib = dir(binaryFile);
                    count = count+1;
                    if count >= maxCount
                        msg = message...
                              ('coder_compile:CoderCompile:ProductNotFound', ...
                               buildResults(i).EvaluatedMakeCommand, binaryFile, ...
                               num2str(this.MaxWaitTimeInSeconds));
                        error(msg.Identifier, '%s', string(msg));
                    end
                end
            end
        end
    end
end



function dirLibBefore = i_getDirLibBefore(libNameWithPath)
% Obtain a directory listing of all files that match the library name (with
% wildcard instead of the actual suffix)
    
% Add a wildcard so that the directory listing picks up any file starting
% with the library name
libNameWithPath = [libNameWithPath '*'];
dirLibBefore = dir(libNameWithPath);
end
