Is there any way (other than the manual one) to grab all the functions a notebook uses from the different user-defined packages and putting everything into one file to deploy the code for e.g. CDF? I understand that this involves a recursive search through a possibly large number of package files, and the discovery of heavy interrelations between functions, but it is definitely not impossible to be done (I've done it myself many times by hand), and thus Mathematica should be able to do it. Perhaps Wolfram Workbench?
2 Answers
I am not aware of such functionality "out of the box", but you can use various symbol dependency frameworks (I have my version published here, although at present it does contain some bugs), to figure out a set of symbols being used. You will need a few auxiliary functions to extract all symbols used in a notebook, and prepare boxed form of the code for them, from their global properties.
Here is some code which can get you started:
ClearAll[getCodePieces];
getCodePieces[nb : (_NotebookObject | Automatic) : Automatic] :=
With[{nbl = If[nb === Automatic, EvaluationNotebook[], nb]},
Flatten@
Cases[NotebookGet[nbl], Cell[BoxData[boxes_], "Input", ___] :>
ToExpression[boxes, StandardForm, HoldComplete], Infinity]];
ClearAll[getDependentSymbols];
getDependentSymbols[nb : (_NotebookObject | Automatic) : Automatic] :=
Union@Flatten[depends /@ getCodePieces[nb]];
ClearAll[createCodeBoxes];
createCodeBoxes[HoldComplete[sym_Symbol]] :=
Module[{optionCode, attributeCode, dsocode, ocode, ucode, boxDefs},
optionCode =
If[# === {}, {}, MakeBoxes[Options[sym] = #]] &@
Options[Unevaluated[sym]];
attributeCode =
If[# === {}, {}, MakeBoxes[SetAttributes[sym, #]]] &@
Attributes[sym];
boxDefs[defOper_] :=
If[# === {}, {},
Replace[#, (Verbatim[HoldPattern][lhs_] :> rhs_) :>
MakeBoxes[defOper[lhs, rhs]], {1}] &@#
] &;
dsocode =
boxDefs[SetDelayed][
Flatten[{OwnValues[sym], DownValues[sym], SubValues[sym]}]];
ucode = boxDefs[UpSetDelayed][UpValues[sym]];
If[# === {}, {}, RowBox[Join[#, {";", "\n"}]]] &@
Flatten@
Riffle[Flatten[{optionCode, attributeCode, ucode,dsocode }],
{";", "\n"}]];
Clear[makeNbCode];
makeNbCode[nb : (_NotebookObject | Automatic) : Automatic] :=
Cell[BoxData[#], "Input"] &@
RowBox[Flatten[createCodeBoxes /@ getDependentSymbols[nb]]];
This depends on my dependency-tracking framework, particularly the depends
function, from the mentioned answer. You use it as (I assume the evaluation notebook for simplicity):
CellPrint@makeNbCode[]
Note that the above code is not complete and will miss certain definitions (e.g. those for Format
, default values, etc) - so treat this as a starting point. I tested this on a few examples, seems to work fine. Note also that this will keep the symbol names, including the namespaces / contexts they belong to. Finally, not that one can not fully reconstruct the original code from global properties, bacause the global properties do not encode the information on whether or not the definition was given as immediate or delayed. This should not matter much here, since definitions are used in the way they are stored in the global rule base, though.
An alternative approach would be to get the set of symbols your notebook depends on, as above, but then, rather than reconstructing code from in-memory definitions, find the set of packages you need, load them as Get[...,"HeldExpressions"]
, and then search the held code of those packages. This may be somewhat more robust, but a little more complex to implement well (although not really very difficult).
Both approaches rely on the fact that your kernel state reflects the fully-working (loaded into memory) functionality that you want to extract - so the recommended use is to first load everything and run your functionality, and then use any of the above methods. If you want to have more "static analysis", this is also possible, but again a bit more complex, and your notebook must be self-contained (loading all needed packages, etc).
-
$\begingroup$ This is exactly along the lines I have visualized. Will try to modify/apply it in near future. I probably will have some questions though. $\endgroup$ Commented Mar 6, 2012 at 16:16
-
$\begingroup$ @Istvan Sounds great. I actually usually prefer the explicit code (top-level) solutions, since they give you both more control and more understanding. It is however often hard to make them robust. As to questions - always welcome (if not me, surely lots of other folks here will be able to help) - and you also know my email (on gmail). $\endgroup$ Commented Mar 6, 2012 at 16:24
-
$\begingroup$ This is useful, and I totally forgot it. One problem is though that if you have anything more complex than a
Manipulate
(e.g.DynamicModule
), you are stuck again. $\endgroup$ Commented Mar 6, 2012 at 14:15 -
$\begingroup$ @Istvan I actually took it as you really want the code you are using to be collected in the notebook, to have more control over it - this is why I was suggesting rather complex solutions. If all you have is
Manipulate
, and you don't care that the reader won't see the code for other symbols easily, the surelySaveDefinitions
is the way to go. $\endgroup$ Commented Mar 6, 2012 at 14:25
Language`ExtendedFullDefinition
could be exploited to achieve the intended mechanism (recursive collection of related symbols and definitions), with the caveat that theLanguage
context is not documented. $\endgroup$