function is_ok = cosmo_publish_run_scripts(varargin)
% helper function to publish example scripts (for developers)
%
% cosmo_publish_build_html([force|fn])
%
% Inputs:
% fn filename of matlab file to publish (in the examples
% directory)
% '-force' force rebuild, even if an output file is newer than the
% corresponding output file
% '-dry' dry run: do not build any files, but show the output that
% would be shown.
% '-o', d write output in directory d. By default d is
% 'doc/source/_static/publish/' relative to the CoSMoMVPA
% root directory.
%
% Notes:
% - if no filename is given, then all files are rebuilt if necessary
% - this function is intended for developers (to build the website)
% - whether an output file is out of date is first determined using
% git: if git shows no changes to the last commit of the input file
% then it is assumed it is not out of date. If the input file has
% been changed since the last commit (or never been committed), then
% the modification date is used to determine whether it is out of date.
% - requirements
% * a Unix-like system
% * a working installation of git
% * CoSMoMVPA code present in a git repository
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
% ensure 'publish' function is available
cosmo_check_external('!publish', true);
[srcfn_cell, opt] = process_input(varargin{:});
trgdir = get_output_dir(opt);
trgext = ['.' opt.format];
summaryfn = ['index.' opt.format];
orig_path = path();
path_resetter = onCleanup(@()path(orig_path));
% go to mvpa dir
orig_pwd = pwd();
pwd_resetter = onCleanup(@()cd(orig_pwd));
mvpa_dir = fileparts(mfilename('fullpath'));
cd(mvpa_dir);
if ~isdir(trgdir) && ~opt.dryrun
mkdir_recursively(trgdir);
end
nsrc = numel(srcfn_cell);
outputs = cell(nsrc, 1);
total_time_took = 0;
output_pos = 0;
is_ok = true;
for k = 1:nsrc
srcfn = srcfn_cell{k};
[srcpth, srcnm] = fileparts(srcfn);
if k == 1
addpath(srcpth);
end
trgfn = fullfile(trgdir, [srcnm trgext]);
[needs_update, build_msg] = target_needs_update(srcfn, trgfn);
if opt.force
build_msg = sprintf('update forced: %s', srcfn);
end
fprintf(build_msg);
do_update = needs_update || opt.force;
if do_update
fprintf('\n building ... ');
clock_start = clock();
if opt.dryrun
fprintf('<dry run>');
is_built = true;
else
is_built = publish_helper(srcfn, trgfn);
end
clock_end = clock();
time_took = etime(clock_end, clock_start);
total_time_took = total_time_took + time_took;
if is_built
outcome_msg = ' done';
else
outcome_msg = ' !! failed';
is_ok = false;
end
status_msg = sprintf('%s (%.1f sec)', outcome_msg, time_took);
else
status_msg = '';
end
fprintf('%s\n', status_msg);
output_pos = output_pos + 1;
outputs{output_pos} = srcnm;
end
fprintf('Processed %d files (%.1f sec)\n', output_pos, total_time_took);
outputfn = fullfile(trgdir, summaryfn);
if ~opt.dryrun
write_index(outputfn, opt);
end
function write_index(index_outputfn, opt)
[outputdir, outputnm] = fileparts(index_outputfn);
inputdir = fullfile(get_root_dir(), 'examples');
d = dir(fullfile(inputdir, '*.m'));
fid = fopen(index_outputfn, 'w');
cleaner3 = onCleanup(@()fclose(fid));
fprintf(fid, ['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"'...
'>\n']);
fprintf(fid, ['<HTML><HEAD><TITLE>Index of matlab outputs'...
'</TITLE></HEAD>\n<BODY>Matlab output<UL>\n']);
for k = 1:numel(d)
[unused, nm] = fileparts(d(k).name);
outputfn = sprintf('%s.%s', nm, opt.format);
if exist(fullfile(outputdir, outputfn), 'file')
fprintf(fid, '<LI><A HREF="%s">%s</A></LI>\n', outputfn, nm);
end
end
fprintf(fid, ['</UL>Back to <A HREF="../../%s.html">index'...
'</A>.</BODY></HTML>\n'], outputnm);
fprintf('Index written to %s\n', index_outputfn);
function trgdir = get_output_dir(opt)
trgdir = opt.output_dir;
if isempty(trgdir)
trgdir = fullfile(get_root_dir(), 'doc/source/_static/publish/');
end
function d = get_root_dir()
d = fileparts(fileparts(mfilename('fullpath')));
function is_built = publish_helper(srcfn, trgfn)
is_built = false;
try
publish_wrapper(srcfn, trgfn);
is_built = true;
catch
me = lasterror();
if exist(trgfn, 'file')
delete(trgfn);
end
msg = sprintf('Unable to build %s: %s\n', srcfn, ...
me.message);
s = me.stack;
for j = 1:numel(s)
msg = sprintf('%s\n %s:%s', msg, s(j).file, s(j).line);
end
cosmo_warning('%s', msg);
end
function publish_wrapper(srcfn, trgfn)
[srcdir, srcnm] = fileparts(srcfn);
trgdir = fileparts(trgfn);
orig_pwd = pwd();
pwd_resetter = onCleanup(@()cd(orig_pwd));
orig_fig_handles = findobj('Type', 'figure');
fig_cleaner = onCleanup(@()close(setdiff(findobj('Type', 'figure'), ...
orig_fig_handles)));
addpath(srcdir);
cd(trgdir);
if cosmo_wtf('is_matlab')
args = {struct('outputDir', trgdir, 'catchError', false)};
else
args = {'format', 'html', 'imageFormat', 'jpg'};
end
publish(srcnm, args{:});
function [srcfn_cell, opt] = process_input(varargin)
% set defaults
srcpat = '';
opt = struct();
opt.force = false;
opt.dryrun = false;
opt.format = 'html'; % currently not changeable
opt.output_dir = '';
% process arguments
n = numel(varargin);
k = 0;
while k < n
k = k + 1;
arg = varargin{k};
if ischar(arg)
if strcmp(arg, '-force')
opt.force = true;
elseif strcmp(arg, '-dry')
opt.dryrun = true;
elseif strcmp(arg, '-o')
if k == n
error('Missing argument after %s', arg);
end
k = k + 1;
opt.output_dir = varargin{k};
elseif ~isempty(srcpat)
error('multiple inputs found, this is not supported');
else
srcpat = arg;
end
else
error('Illegal argument at position %d - expected string', k);
end
end
srcfn_cell = get_srcfn_cell(srcpat);
function srcfn_cell = get_srcfn_cell(srcpat)
default_pattern = {'run_*', 'demo_*'};
if isdir(srcpat)
p = srcpat;
nms = default_pattern;
e = '.m';
else
[p, nm, e] = fileparts(srcpat);
if isempty(p)
p = fullfile(get_root_dir(), 'examples');
end
if isempty(nm)
nms = default_pattern;
else
nms = {nm};
end
if isempty(e)
e = '.m';
end
end
ds = cellfun(@(nm)dir(fullfile(p, [nm e])), nms, 'UniformOutput', false);
d = cat(1, ds{:});
n = numel(d);
if n == 0
error('No input files found');
end
srcfn_cell = cellfun(@(fn)fullfile(p, fn), {d.name}, 'UniformOutput', false);
function [tf, msg] = target_needs_update(srcfn, trgfn)
% helper function to see if html is out of date
[unused, root, srcext] = fileparts(srcfn);
[unused, root_alt, trgext] = fileparts(trgfn);
assert(isequal(root, root_alt));
srcname = [root, srcext];
trgname = [root, trgext];
t_trg = time_last_changed(trgfn);
if isnan(t_trg)
% does not exist, so needs update
tf = true;
msg = sprintf('not found: %s', trgname);
return
end
if is_in_staging(srcfn) || is_untracked(srcfn)
% changes since last commit, see when changes were made
t_src = time_last_changed(srcfn);
tf = isnan(t_src) || t_trg < t_src;
msg = sprintf('modified: %s', srcname);
else
% no changes since last commit, see when last commit was made
t_src = time_last_commit(srcfn);
tf = isnan(t_src) || t_trg < t_src;
msg = sprintf('recent commit: %s', srcname);
end
if ~tf
msg = sprintf('up to date: %s', trgname);
end
function t = time_last_changed(fn)
d = dir(fn);
if isempty(d)
t = NaN;
return
end
assert(numel(d) == 1);
t = (d.datenum - datenum(1970, 1, 1)) * 86400;
function r = run_git(args)
prefix = 'export TERM=ansi; git ';
cmd = [prefix args];
[e, r] = unix(cmd);
if e
fprintf(2, 'Unable to run git, or an error was produced\n');
error(r);
end
function tf = is_untracked(srcfn)
untracked = run_git('ls-files . --exclude-standard --others');
tf = cosmo_match({srcfn}, untracked);
function t = time_last_commit(srcfn)
cmd = sprintf('log -n 1 --pretty=format:%%ct -- %s', srcfn);
r = run_git(cmd);
t = str2double(regexp(r, '(\d*)', 'match', 'once'));
if isempty(t)
t = NaN;
end
assert(numel(t) == 1);
function tf = is_in_staging(fn)
in_staging_str = run_git('diff HEAD --name-only | xargs basename');
in_staging = cosmo_strsplit(in_staging_str, '\n');
basefn = cosmo_strsplit(fn, filesep, -1);
tf = cosmo_match({basefn}, in_staging);
function mkdir_recursively(trgdir)
parent = fileparts(trgdir);
if ~isdir(parent)
mkdir_recursively(parent);
end
mkdir(trgdir);