Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

RFC: Plake, a target-based file assembler

by arbingersys (Pilgrim)
on Apr 03, 2008 at 16:38 UTC ( #678202=perlmeditation: print w/replies, xml ) Need Help??

Plake is a tool that allows you to maintain sections within a single file (usually, variations of the same code/markup/content) and then assemble variations of that file according to which target you call. It was inspired by Make, used in conjunction with Make (by me), and written in Perl, hence the name "Plake".

It uses a very simple grammar, and allows you to define sections within a given text file, then define targets that assemble the sections you want, and optionally, specify a path where the assembled section will be written to disk as a new file.

Plake runs in two modes.

1. "Top-down/lazy", the default mode, which includes all sections for a given target plus lines from the file that do not have a defined section. The output is assembled top-down. This is useful for when you only need small variations of a file to be made, and want to keep Plake to a minimum.

2. "Stickler", which means that any text not attributed to a named section is ignored, and sections are assembled in the order that they are passed into target(). This means you have specific ideas about how the output will be assembled and don't want Plake to try and be intelligent about it.


Plake - Target based file assembler


Here's roughly what a Plake file would look like:
!plake: # Denote "code" section, where we setup targets # This is all just Perl code that gets eval'd... my $START = "header base_section"; target( default, # Target name "$START section1 footer", # Sections to include 'path/to/output/' # Target output path ); target( section2, "$START section2 footer", '' # Output to STDOUT ); !plake. # End section This text will appear if "lazy/top-down" behavior is invoked -- that is, any lines not specifically part of a section are allowed to be output. !plake header HEADER: One liner section !plake base_section: ... included in all assemblages ... ... etc ... !plake section1: ... something, code, markup, comments, whatever ... ... more ... ... etc ... !plake section2: # Start of this section denotes end of previous ... stuff ... ... more ... ... etc ... !plake footer: (c) 2999
And here's generally how you would use the package to process a Plake file:
use Plake qw(target my_builder); # ... Open file... # Parse while(<F>) { Plake::parse($_); } # Setup targets (and whatever else) eval $Plake::CODE; # Build a target. $src is the assembled content. $target is # the path to the output file that should be created, if one # is defined. $stickler_mode = 'on'; my ($target, $src) = Plake::build('target_name', $stickler_mode); # ... Now, do something with $target and $src ...

Plake grammar

Code section
Use !plake: to indicate the start of a ``code'' section, where you declare your targets with the target() method, and maybe even override the default ``build'' method with your own using my_builder().
Standard section
!plake \w+:, e.g. !plake section1:, indicates the start of a multi-line, standard section. The start of a new section flags the end of a previous section.
One-line section
Use !plake \w+, e.g. !plake oneliner (followed immediately by your line) to generate a single line section.
Section ending
Use !plake. to end a section explicitly. You can use it to end standard sections as well as code sections.

I use Plake in a particular software project to generate variations of a file for different implementation scenarios.

For instance, I have a demo online, and it uses a real live SQL password I can't include with the default distro. For the default distro, I use "password" for the password. I also have verbose logging turned off for my demo and the default distro, but in my own testing environment, I want it on even though it generates lots of log activity.

These are all minor variations of the single file for my project. With Plake, I add a target to my Makefile like

configfiles: file=/path/to/Config.plk target=default file=/path/to/Config.plk target=demo

and simply "make configfiles" to get the variations of for uploading to the demo, adding to the distro, etc. Meanwhile, every orientation is very accessible within the single Config.plk file.

Please don't think that Plake was designed merely for overriding default values in config files (670323). There are certainly better ways for doing this. I see it primarily as a development tool. Here's a few places where I thought it might be useful:

- Setting variations for builds, like what I describe above for the config file. A convenience for me since I have yet to implement a more complex (i.e. overrides) configuration system, but still have to make subtle changes (usually, by hand-editing) for various implementations at various stages of development.

- Assemble C/C++ files for specific platforms, in the stead of #ifdef, etc. The resulting .c/.cpp/.h file would be assembled dynamically when the project was make'd for a given platform, just prior to compilation. The code generated for that platform would be a bit simpler to review, since it only includes code that a person cares about in that build.

- Remove experimental features, stubs, or extra debugging from code prior to generating distros, i.e. "Cleanup".

- Branching, like what source control does. You could keep some client or "branch" specific features out of a specific build, but still maintain it in a single file.

- Template variations, like letter writing. Instead of a single boiler plate template, you have targets like "standard_greeting", "enthusiastic_greeting", "familiar_greeting", etc.

- Target-based programming for Perl. Sort of a side-effect, and one I don't see all the ramifications of, but you could use Plake to assemble code targets wholly or partially independent of each other by storing Perl code in a Plake file and doing an eval against the assembled content for a given target. (Just think -- you could keep your entire project of hundreds of modules and code files all in one single, massive text file! I can see everyone lining up now... :)

Plake file:
!plake: target('helowrld', "helowrld", ''); target('oneplus', "oneplus", ''); target('both', "helowrld oneplus", ''); !plake helowrld print "helowrld\n"; !plake oneplus: # Add value to one print 1+3.14, "\n";
Eval'ing code from targets:
perl t\ file="t/plakeval.plk" target="helowrld" helowrld perl t\ file="t/plakeval.plk" target="oneplus" 4.14 perl t\ file="t/plakeval.plk" target="both" helowrld 4.14

If you've made it this far, you might want to check it out yourself:

One thing I'm still pondering: Should a target be able to include other targets? This would be very Make-like, with real dependencies, but I don't know how useful it would actually be.

A blog among millions.

Replies are listed 'Best First'.
Re: RFC: Plake, a target-based file assembler
by dragonchild (Archbishop) on Apr 03, 2008 at 17:58 UTC
    One thing I'm still pondering: Should a target be able to include other targets? This would be very Make-like, with real dependencies, but I don't know how useful it would actually be.

    Add it. Remember - dependency-chaining allows you to say "Only work when that other thing works." That's not useful?

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

      The reason I wasn't sure is because Plake, used as a simple file assembler, is only assembling static sections. So there is no "working" in this sense, i.e. nothing really can fail. The sections just get stuck together.

      If Plake gets used in the "target-based programming" way then I can see that dependency-chaining would become more valuable.

      Really, it wouldn't be hard to add, outside of the problem that would arise when a target and a section share the same name. Thanks.

      A blog among millions.
Re: RFC: Plake, a target-based file assembler
by Anonymous Monk on Apr 04, 2008 at 04:07 UTC

      Hm. Kind of a grab-bag here. I'm assuming that the implication is "these do what you need". But I don't see the correlation, except that some of the tools listed are used to augment Make.

      PANT is for generating and running a set of pre-determined commands. I see no ability to assemble file variations.

      Makefile-Parser does what it sounds like. Plake is not tied to Make, it's just very useful with Make.

      makepp: "Compatible but improved replacement for make"

      Template::Toolkit. As far as I'm aware, template systems do not exhibit behavior themselves for creating targets. I did toy with the idea of using a template system to write the Plake (.plk) files in, but that seemed like too much tool for the job.

      A blog among millions.

        Template::Toolkit. As far as I'm aware, template systems do not exhibit behavior themselves for creating targets.

        The trouble is that most of the example applications you discuss sound a lot like things that could be easily done using a templating system -- and you probably would've done it that way if you were used to thinking in those terms instead of being used to messing with make files.

        make is a pretty odd tool, and myself I'm not that sure that the make idiom really has all that much going for it... and the one thing it does have going for it is sorting out dependencies (e.g. if you need to do this, do that too; if that's been done already, don't bother with it) and you seem to have decided to skip that feature for this tool.

        Just for arguments sake, what would happen if you wanted to just write a perl script to drive the process without using Plake? Wouldn't it be something like:

        my $target = shift; my $password; if ($target eq 'dev') { $password = 'real password'; } elsif ($target eq 'release') { $password = 'change me'; } else { # default $password = 'change me'; } do_build( $password );

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://678202]
Approved by Corion
Front-paged by Arunbear
[choroba]: Snow here. Good morning nonetheless.
[Corion]: A good morning to you too, Discipulus!
[Discipulus]: ah! buran stroke again?
[choroba]: Not sure what meteorological event it's been, but it's -6 now and lots of snow around.

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (3)
As of 2018-03-19 08:06 GMT
Find Nodes?
    Voting Booth?
    When I think of a mole I think of:

    Results (236 votes). Check out past polls.