App-makefilepl2cpanfile

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

### Parse\_Prereqs

```perl
    parse_prereqs : (Str ∪ {⊥}) -> DepMap

    parse_prereqs(s) ==
      if s = ⊥ ∨ is_ref(s) then ∅
      else simple(s') ⊕ₗ structured(s') ⊕ₗ legacy(s')
      where s' == s with every '#' comment removed

    simple(s)     == ⋃ { {PHASE_MAP(k) ↦ {requires ↦ pairs(b)}}
                         | k ∈ {PREREQ_PM, BUILD_REQUIRES, TEST_REQUIRES, CONFIGURE_REQUIRES},
                           b ∈ blocks(k, s) }
    structured(s) == ⋃ { {p ↦ {r ↦ pairs(b)}}
                         | P ∈ blocks(prereqs, s), (p ↦ (r ↦ b)) ∈ P, p ∈ Phase, r ∈ Rel }
    legacy(s)     == ⋃ { {first(LEGACY(k)) ↦ {second(LEGACY(k)) ↦ pairs(b)}}
                         | k ∈ dom LEGACY, b ∈ blocks(k, s),
                           ¬ (∃ P ∈ blocks(prereqs, s) • b ⊆ P) }
    pairs(b)      == { m ↦ ⟨ if v ∈ Requirement then v else 0, comment(m, b) ⟩
                         | ('m' => v) ∈ b, m ∈ ModName }
    -- comment(m, b): the line's comment if m is the last valid entry on it

    post  ∀ p ↦ R ∈ result • R ≠ ∅ ∧ (∀ r ↦ M ∈ R • M ≠ ∅)
          ∧ no I/O ∧ no warnings
```

### Generate

```perl
    Args ≙ [ content : Str ∪ {⊥}; makefile : Str; existing : Str; with_develop : 𝔹 ]

    generate : Args × Env ⇸ Str

    pre   (a.content ≠ ⊥ ∨ string(a.makefile) ∈ dom E.fs ∧ regular(a.makefile) ∧ readable(a.makefile))
          ∧ (a.with_develop ⇒ cfg(E) ∉ dom E.fs ∨ ¬ regular(cfg(E)) ∨ yaml_ok(cfg(E)))

    generate(a, E) ==
      let content == if a.content ≠ ⊥ then a.content else decode_utf8_or_raw(E.fs(a.makefile))
          deps    == parse_prereqs(content)
          kept    == { r ↦ { m ↦ ⟨ if v ∈ Requirement then v else 0, ⊥ ⟩ }
                       | (r m v) ∈ develop_entries(a.existing) \ comments, m ∈ ModName }
          dev     == deps ⊕ₗ {develop ↦ kept}
          listed  == ⋃ { dom(dev(develop)(r)) | r ∈ Rel }
          tools   == if E.home = ⊥ ∨ E.home = "" ∨ ¬ regular(cfg(E)) then DEFAULT_DEVELOP
                     else valid_entries(yaml(cfg(E)).develop)
          final   == if a.with_develop
                     then dev ⊕ₗ {develop ↦ {requires ↦ { m ↦ ⟨v, ⊥⟩ | (m ↦ v) ∈ tools, m ∉ listed }}}
                     else dev
      in  emit(final, min_perl(content))

    post  result ∈ Str
          ∧ prefix(result, HEADER ⁀ "\n") ∧ last(result) = '\n' ∧ ¬ suffix(result, "\n\n")
          ∧ E′ = E                           -- nothing is written
          ∧ $@′ = $@ ∧ $!′ = $! ∧ $_′ = $_
          -- every value from a file or the environment in a message is
          -- printable(v): control and bidi characters as \x{..}

    -- Failure cases (the function dies):
    a.content = ⊥ ∧ (¬ regular(a.makefile) ∨ ¬ readable(a.makefile))  ⇒  croak("Cannot read '" ⁀ a.makefile ⁀ "'")
    a.with_develop ∧ regular(cfg(E)) ∧ ¬ yaml_ok(cfg(E))  ⇒  croak("Failed to parse " ⁀ cfg(E) ⁀ ": " ⁀ err)
    a.with_develop ∧ stat(cfg(E)) fails with errno ∉ {ENOENT, ENOTDIR}  ⇒  croak("Failed to parse " ⁀ cfg(E) ⁀ ": " ⁀ errno)
```

## State Diagram

The module keeps no state between calls: every call starts at START and
ends at RETURN or at an error.  The diagram shows the states that one
call to `generate()` passes through.  `parse_prereqs()` is the single
step PARSE.

```perl
    +-------+
    | START |  generate(%args) is called
    +-------+
        |
        | makefile is a readable regular file?
        |---- no ------------------------------------> [DIE] croak "Cannot read '...'"
        | yes
        v
    +------------+  read as UTF-8
    | READ_UTF8  |---- decode error --> +-----------+  carp "invalid UTF-8"
    +------------+                      | READ_RAW  |  (read the raw bytes)
        |     \                         +-----------+
        |      \                             |---- raw read fails --> [DIE] error passed on
        |       \---- other I/O error --------------------------> [DIE] error passed on
        | ok                                 |
        v                                    |
    +------------+ <-------------------------+
    |   PARSE    |  parse_prereqs(content); find MIN_PERL_VERSION
    +------------+  (pure: no I/O, no warnings)
        |
        | existing has an on 'develop' section?
        |---- no ----------------------------+
        | yes                                |
        v                                    |
    +------------+  copy entries; drop       |
    |   MERGE    |  bad names; carp and      |
    +------------+  drop bad versions        |
        |                                    |
        +<-----------------------------------+
        |
        | with_develop true?
        |---- no ----------------------------------------------+
        | yes                                                  |
        v                                                      |
    +------------+  no home dir, or config path missing        |
    | CONFIG     |  or not a regular file -------> DEFAULTS    |
    +------------+                                   |         |
        | config is a regular file                   |         |
        |---- stat/read/YAML error --> [DIE] croak "Failed to parse ..."
        v                                            |         |
    +------------+  no develop: key --> carp --> DEFAULTS      |
    | VALIDATE   |  bad name    --> carp, skip entry           |
    +------------+  bad version --> carp, version 0            |
        |                                            |         |
        v                                            v         |
    +------------+ <---------------------------------+         |
    |   INJECT   |  add tools not already in develop           |
    +------------+                                             |
        |                                                      |
        v                                                      |



( run in 0.598 second using v1.01-cache-2.11-cpan-28bccf0f6e5 )