# # # add_dir "tests/catch_lua_output" # # add_file "tests/catch_lua_output/__driver__.lua" # content [f89a1951954b516efa7cfd39e24a1a045140267a] # # add_file "tests/catch_lua_output/hooks.lua" # content [be1023311664e995bb504fcd33e51f791853f7a3] # # add_file "tests/catch_lua_output/hooks_automate.lua" # content [44865f0062e822c28c15ba03d0197c253fe4fba6] # # patch "lua_hooks.cc" # from [d4f06492ebfd19893156cf0021b7afef29009332] # to [b549dbc88019ef637da9a1cd2205162485b1d0b2] # # patch "monotone.texi" # from [b77d61e56958096acec3af24178c9022dbd75e95] # to [ee7c1189daee41efbcbdcb7f306d9bc02ca6c81f] # ============================================================ --- tests/catch_lua_output/__driver__.lua f89a1951954b516efa7cfd39e24a1a045140267a +++ tests/catch_lua_output/__driver__.lua f89a1951954b516efa7cfd39e24a1a045140267a @@ -0,0 +1,34 @@ + +mtn_setup() + +check(get("hooks.lua")) + +check(mtn("automate", "select", "*", "--rcfile", "hooks.lua"), 0, true, true) + +check(qgrep("lua: this is catched", "stderr")) +check(qgrep("lua: this is also catched", "stderr")) + +check(qgrep("lua: line breaks", "stderr")) +check(qgrep("lua: are handled", "stderr")) +check(qgrep("lua: properly as well", "stderr")) + +check(qgrep("this is not catched", "stdout")) +check(not qgrep("lua: this is not catched", "stderr")) + +check(qgrep("this is also not catched", "stderr")) +check(not qgrep("lua: this is also not catched", "stderr")) + +-- load a slightly different version with no regular stdout output +-- which would confuse the stdio parser +check(get("hooks_automate.lua")) + +check(mtn("automate", "stdio", "--rcfile", "hooks_automate.lua"), 0, true, true, "l6:select1:*e") +check(qgrep("this is also not catched", "stderr")) + +include("common/automate_stdio.lua") +progress = parse_stdio(readfile("stdout"), 0, nil, 'p') + +check(progress[1] == "lua: this is catched") +check(progress[2] == "lua: this is also catched") +check(progress[3] == "lua: line breaks\nlua: are handled\nlua: properly as well") + ============================================================ --- tests/catch_lua_output/hooks.lua be1023311664e995bb504fcd33e51f791853f7a3 +++ tests/catch_lua_output/hooks.lua be1023311664e995bb504fcd33e51f791853f7a3 @@ -0,0 +1,10 @@ +function expand_selector(...) + print("this is catched") + io.write("this is also catched") + print("line breaks\nare handled\nproperly as well") + + io.stdout:write("this is not catched\n") + io.stderr:write("this is also not catched\n") + + return nil +end ============================================================ --- tests/catch_lua_output/hooks_automate.lua 44865f0062e822c28c15ba03d0197c253fe4fba6 +++ tests/catch_lua_output/hooks_automate.lua 44865f0062e822c28c15ba03d0197c253fe4fba6 @@ -0,0 +1,11 @@ +-- this is equal to hooks.lua, except that we don't write to stdout +-- which would confuse the stdio parser +function expand_selector(...) + print("this is catched") + io.write("this is also catched") + print("line breaks\nare handled\nproperly as well") + + io.stderr:write("this is also not catched\n") + + return nil +end ============================================================ --- lua_hooks.cc d4f06492ebfd19893156cf0021b7afef29009332 +++ lua_hooks.cc b549dbc88019ef637da9a1cd2205162485b1d0b2 @@ -30,6 +30,7 @@ #include "cmd.hh" #include "commands.hh" #include "globish.hh" +#include "simplestring_xform.hh" // defined in std_hooks.c, generated from std_hooks.lua extern char const std_hooks_constant[]; @@ -77,6 +78,38 @@ extern "C" lua_pushnil(LS); return 1; } + // taken from http://medek.wordpress.com/2009/02/03/wrapping-lua-errors-and-print-function/ + static int + monotone_message(lua_State *LS) + { + int nArgs = lua_gettop(LS); + lua_getglobal(LS, "tostring"); + + std::string ret; + for (int i = 1; i <= nArgs; ++i) + { + const char *s; + lua_pushvalue(LS, -1); + lua_pushvalue(LS, i); + lua_call(LS, 1, 1); + s = lua_tostring(LS, -1); + if (s == NULL) + return luaL_error( + LS, LUA_QL("tostring") " must return a string to ", LUA_QL("print") + ); + + if (i > 1) + ret.append("\t"); + + ret.append(s); + lua_pop(LS, 1); + } + + string prefixed; + prefix_lines_with(_("lua: "), ret, prefixed); + P(F("%s") % prefixed); + return 0; + } } app_state* @@ -116,6 +149,7 @@ lua_hooks::lua_hooks(app_state * app) luaL_openlibs(st); lua_register(st, "get_confdir", monotone_get_confdir_for_lua); + lua_register(st, "message", monotone_message); add_functions(st); // Disable any functions we don't want. This is easiest @@ -132,6 +166,21 @@ lua_hooks::lua_hooks(app_state * app) "")) throw oops("lua error while disabling existing functions"); + // redirect output to internal message handler which calls into + // our user interface code. Note that we send _everything_ to stderr + // or as out-of-band progress stream to keep our stdout clean + static char const redirect_output[] = + "io.write = function(...) " + " message(...) " + "end " + "print = function(...) " + " message(...) " + "end "; + + if (!run_string(st, redirect_output, + "")) + throw oops("lua error while redirecting output"); + map_of_lua_to_app.insert(make_pair(st, app)); } ============================================================ --- monotone.texi b77d61e56958096acec3af24178c9022dbd75e95 +++ monotone.texi ee7c1189daee41efbcbdcb7f306d9bc02ca6c81f @@ -38,7 +38,7 @@ Copyright @copyright{} 2006 Jeronimo Pellegrini @* Copyright @copyright{} 2006 Alex Queiroz @* Copyright @copyright{} 2006, 2007 William Uther @* -Copyright @copyright{} 2006 - 2009 Thomas Keller @* +Copyright @copyright{} 2006 - 2010 Thomas Keller @* Copyright @copyright{} 2007 - 2010 Stephen Leake @* This manual is made available under the GNU GPL version 2.0 or @@ -79,7 +79,7 @@ @top Overview * Advanced Uses:: Going beyond the basics * CVS Phrasebook:: Transitional guide for CVS users * Command Reference:: Details of each monotone command -* Hook Reference:: Functions which extend monotone +* Lua Reference:: How Lua is integrated in monotone * Special Topics:: Extra explanations and details * Default hooks:: The standard hook definitions * General Index:: Index of concepts and functions @@ -2444,7 +2444,7 @@ @section Network Service Revisited main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the @file{~/.monotone/read-permissions} on the server, above -the broader permissions given to team-members. See the @ref{Hook +the broader permissions given to team-members. See the @ref{Lua Reference} for @code{get_netsync_read_permitted} for more details; the resulting file looks like this: @@ -2842,7 +2842,7 @@ @heading Selector expansion @code{a:jrh@@example.org}. This hook will generally assign a selector type to values which ``look like'' partial hex strings, email addresses, branch names, or date specifications. For the complete -source code of the hook, see @ref{Hook Reference}. +source code of the hook, see @ref{Lua Reference}. @heading Expanding dates @@ -2873,7 +2873,7 @@ @heading Expanding dates The time component, if supplied, must be complete to the second. @end table -For the complete source code of the hook, see @ref{Hook Reference}. +For the complete source code of the hook, see @ref{Lua Reference}. @heading Typeless selection @@ -3039,7 +3039,7 @@ @section Inodeprints Normally, instead of enabling this up on a per-workspace basis, you will want to simply define the @code{use_inodeprints} hook to return @code{true}; this will automatically enable inodeprints mode in any new -workspaces you create. See @ref{Hook Reference} for details. +workspaces you create. See @ref{Lua Reference} for details. @page @node Merge Conflicts, Workspace Collisions, Inodeprints, Advanced Uses @@ -3525,7 +3525,7 @@ @section Quality Assurance deemed ``acceptable'' does monotone actually select an update target to merge into your workspace. -For details on these hooks, see the @ref{Hook Reference}. +For details on these hooks, see the @ref{Lua Reference}. @page @node Vars, Reserved Files, Quality Assurance, Advanced Uses @@ -3841,7 +3841,7 @@ @section File Attributes You can tell monotone to automatically take actions based on these attributes by defining hooks; see the @code{attr_functions} entry in address@hidden Reference}. Every time your workspace is written to, address@hidden Reference}. Every time your workspace is written to, monotone will run the corresponding hooks registered for each attr in your workspace. This way, you can extend the vocabulary of attrs understood by monotone simply by writing new hooks. @@ -4673,7 +4673,7 @@ @heading Initializing a Repository users. address@hidden Command Reference, Hook Reference, CVS Phrasebook, Top address@hidden Command Reference, Lua Reference, CVS Phrasebook, Top @chapter Command Reference Monotone has a large number of commands. To help navigate through them @@ -5502,7 +5502,7 @@ @section Network In the server, different permissions can be applied to each branch; see the hooks @code{get_netsync_read_permitted} and address@hidden (see @ref{Hook Reference}). address@hidden (see @ref{Lua Reference}). If a @option{--pid-file} option is specified, the command @command{serve} will create the specified file and record the process @@ -5993,7 +5993,7 @@ @section Key and Cert Trust @item mtn trusted @var{id} @var{certname} @var{certval} @var{signers} This command lets you test your revision trust hook address@hidden (see @ref{Hook Reference}). You pass it address@hidden (see @ref{Lua Reference}). You pass it a revision ID, a certificate name, a certificate value, and one or more key IDs or key names, and it will tell you whether, under your current settings, Monotone would trust a cert on that revision with that value signed by @@ -10199,26 +10199,24 @@ @section GIT @end ftable address@hidden Hook Reference, Special Topics, Command Reference, Top address@hidden Hook Reference address@hidden Lua Reference, Special Topics, Command Reference, Top address@hidden Lua Reference -Monotone's behavior can be customized and extended by writing address@hidden functions}, which are written in the address@hidden://www.lua.org, Lua} programming language. At certain points -in time, when monotone is running, it will call a hook function to -help it make a decision or perform some action. If you provide a hook -function definition which suits your preferences, monotone will -execute it. This way you can modify how monotone behaves. +Monotone makes use of the @uref{http://www.lua.org, Lua} programming language +to customize and extend its behaviour. By writing @dfn{functions} which are +loaded and evaluated at runtime, you can therefor help monotone to make a +particular decision, get a suitable default or preference or perform a certain +action. -You can put new definitions for any of these hook functions in a file +You can put new definitions for either use case in a file @file{$HOME/.monotone/monotonerc}, or in your workspace in @file{_MTN/monotonerc}, both of which will be read every time monotone runs. Definitions in @file{_MTN/monotonerc} shadow (override) definitions made in your @file{$HOME/.monotone/monotonerc}. You can also tell monotone to interpret extra hook functions from any other @var{file} -using the @address@hidden option; hooks defined in files -specified on the command-line will shadow hooks from the the automatic -files. +using the @address@hidden option; again, hooks defined in files +specified on the command-line will shadow hooks from the other predefined +locations. By specifying @address@hidden you can automatically load all the files contained into @var{directory}. @@ -10244,16 +10242,24 @@ @chapter Hook Reference only be seen by the new hook. Since in Lua variables default to the global scope, the new hook is seen from inside monotone. -Monotone also makes available to hook writers a number of helper -functions exposing functionality not available with standard Lua. +Monotone also provides a number of helper functions to hook writers +exposing functionality not available with standard Lua. One particular +interesting feature is the ability to extend monotone with custom, Lua based +commands which are listed under the @dfn{user} command group. All you need +to do for this is to write a custom function and use @code{register_command} +to make it visible in monotone's command line interface. The source +distribution contains some example implementations in the address@hidden/command} directory. +See @ref{Additional Lua Functions} for more information on these topics. @menu * Hooks:: All hooks called by monotone. * Additional Lua Functions:: Extra functionality available to hook writers. +* Implementation Differences:: Functional differences from standard Lua @end menu @page address@hidden Hooks, Additional Lua Functions, Hook Reference, Hook Reference address@hidden Hooks, Additional Lua Functions, Lua Reference, Lua Reference @section Hooks This section documents the existing hook functions and their default @@ -11290,7 +11296,7 @@ @subsection Meta Hooks @end ftable @page address@hidden Additional Lua Functions, , Hooks, Hook Reference address@hidden Additional Lua Functions, Implementation Differences, Hooks, Lua Reference @section Additional Lua Functions This section documents the additional Lua functions made available to @@ -11526,7 +11532,34 @@ @section Additional Lua Functions @end ftable address@hidden Special Topics, Default hooks, Hook Reference, Top address@hidden address@hidden Implementation Differences, ,Additional Lua Functions, Lua Reference address@hidden Implementation Differences + +This section documents disabled and overwritten functions from the standard +Lua libraries. + address@hidden @code + address@hidden print, io.write + +Both functions do no longer print to standard output, but to standard +error, formatted as normal progress message with the prefix "lua:" +in front of every line. + +The rationale behind this is to catch most of the informational +messages from hooks which use these functions and make them processable +by monotone's user interface, i.e. allowing them to be logged just as other +progress messages and prevent unexpected out-of-band output in the address@hidden interface. + +If you explicitely need to write to either stdout or stderr, please use address@hidden:write} and @code{io.stderr.write}. + address@hidden ftable + + address@hidden Special Topics, Default hooks, Lua Reference, Top @chapter Special Topics This chapter describes some ``special'' issues which are not directly