# This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git)
# Copyright (c) 2023-2025 Doliam
# This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html).
# This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

import lmake

if __name__!='__main__' :

    import os

    from lmake.rules import Rule,PyRule
    from lmake.rules import python as system_python

    lmake.manifest = (
        'Lmakefile.py'
    ,   'step.py'
    ,   'local_module.py'
    ,   'hello'
    ,   'world'
    ,   'deps.hello+world.ref'
    ,   'interpreter.hello+world.ref'
    ,   'env.hello.ref'
    ,   *(f'autodep.{ad}.ref' for ad in lmake.autodeps)
    ,   'resources.1.ref'
    ,   'resources.2.ref'
    ,   'auto_mkdir.no.ref'
    ,   'auto_mkdir.yes.ref'
    )

    from step         import step
    from local_module import local_func

    class Cmp(Rule) :
        target = r'{File:.*}.ok'
        deps = {
            'DUT' : '{File}'
        ,   'REF' : '{File}.ref'
        }
        cmd = 'diff {REF} {DUT}'

    def file_func () :
        if step==1 : raise RuntimeError
        return File
    def file2_func() :
        if step==1 : raise RuntimeError
        return File2

    class Deps(PyRule) :
        stems = {
            'File1' : r'\w*'
        ,   'File2' : r'\w*'
        }
        target = 'deps.{File1}+{File2}'
        def deps() :
            if step==1 : raise RuntimeError
            return {
                'FIRST'  : File1
            ,   'SECOND' : file2_func()
            }
        def cmd() :
            print(open(deps['FIRST' ]).read(),end='')
            print(open(deps['SECOND']).read(),end='')

    class Interpreter(PyRule) :
        stems = {
            'File1' : r'\w*'
        ,   'File2' : r'\w*'
        }
        target = 'interpreter.{File1}+{File2}'
        deps = {
            'FIRST'  : '{File1}'
        ,   'SECOND' : '{File2}'
        }
        def python() :
            if step==1 : raise RuntimeError
            return (system_python,)
        def cmd() :
            print(open(deps['FIRST' ]).read(),end='')
            print(open(deps['SECOND']).read(),end='')

    class Env(PyRule) :
        target = r'env.{File:\w*}'
        environ           = { 'VAR'           : file_func }
        environ_resources = { 'VAR_RESOURCES' : file_func }
        environ_ancillary = { 'VAR_ANCILLARY' : file_func }
        def cmd() :
            print(os.environ['VAR'          ])
            print(os.environ['VAR_RESOURCES'])
            print(os.environ['VAR_ANCILLARY'])

    class StartDelay(Rule) :
        target = r'start_delay.{File:\w*}'
        force  = True
        def start_delay() :
            if step==1 : raise RuntimeError
            return File=='yes'
        cmd = ''

    class Autodep(Rule) :
        target = r'autodep.{File:\w*}'
        def autodep() :
            if step==1 : raise RuntimeError
            return File
        cmd = 'cat hello'

    class Resources(Rule) :
        target = r'resources.{File:\w*}'
        def resources() :
            if step==1 : raise RuntimeError
            return {'cpu':local_func(File)}
        cmd = "echo {resources['cpu']}"

    class StderrLen(Rule) :
        target = r'max_stderr_len.{File:\w*}'
        force  = True
        def max_stderr_len() :
            if step==1 : raise RuntimeError
            return File
        cmd = ''

    class StderrOk(Rule) :
        target = r'stderr_ok.{File:\w*}'
        def stderr_ok() :
            if step==1 : raise RuntimeError
            return File=='yes'
        cmd = 'echo hello >&2'

    class AutoMkdir(Rule) :
        target    = r'auto_mkdir.{File:\w*}'
        stderr_ok = True
        def auto_mkdir() :
            if step==1 : raise RuntimeError
            return File=='yes'
        cmd = 'cd auto ; cat ../hello ; :'

    class Cmd(Rule) :
        target = 'cmd'
        deps   = { 'HELLO' : 'hello' }
        if step==1 : cmd = 'cat {HELLO} ; echo {bad}'
        else       : cmd = 'cat {HELLO} ; echo {step}'

else :

    import textwrap

    import ut

    print('hello'              ,file=open('hello'                      ,'w'))
    print('world'              ,file=open('world'                      ,'w'))
    print('hello\nworld'       ,file=open('deps.hello+world.ref'       ,'w'))
    print('hello\nworld'       ,file=open('interpreter.hello+world.ref','w'))
    print('hello\nhello\nhello',file=open('env.hello.ref'              ,'w'))
    print(1                    ,file=open('resources.1.ref'            ,'w'))
    print(2                    ,file=open('resources.2.ref'            ,'w'))
    print('hello'              ,file=open('auto_mkdir.yes.ref'         ,'w'))
    #
    for ad in lmake.autodeps : print('hello',file=open(f'autodep.{ad}.ref','w'))
    open('auto_mkdir.no.ref','w')
    #
    print(textwrap.dedent('''
        def local_func(f) : return f
    '''[1:]),file=open('local_module.py','w')) # strip initial \n

    print(f'step=0',file=open('step.py','w'))
    ut.lmake( 'cmd' , done=1 , new=1 )        # create file cmd to ensure transition bad->good does not leave a manual file

    for s in (1,2) :
        print(f'step={s}',file=open('step.py','w'))
        rc = 1 if s==1 else 0
        #
        ut.lmake( 'deps.hello+world.ok'        , done=2-rc*2 , steady=0    , failed=0  , new=1-rc ,                              rc=rc )
        ut.lmake( 'interpreter.hello+world.ok' , done=2-rc*2 , steady=0    , failed=rc , new=3*rc , early_rerun=rc  ,            rc=rc ) # interpreter is a dep
        ut.lmake( 'env.hello.ok'               , done=2-rc*2 , steady=0    , failed=rc , new=rc   , early_rerun=rc  ,            rc=rc )
        ut.lmake( 'start_delay.no'             , done=rc     , steady=1-rc , failed=0  , new=0    , early_rerun=rc  , start=1  , rc=0  )
        ut.lmake( 'start_delay.yes'            , done=rc     , steady=1-rc , failed=0  , new=0    , early_rerun=rc  , start=rc , rc=0  )
        ut.lmake( 'resources.1.ok'             , done=2-rc*2 , steady=0    , failed=rc , new=rc   ,                              rc=rc )
        ut.lmake( 'resources.2.ok'             , done=2-rc*2 , steady=0    , failed=rc , new=rc   ,                              rc=rc )
        ut.lmake( 'max_stderr_len.1'           , done=rc     , steady=1-rc , failed=0  , new=0    , early_rerun=rc  ,            rc=0  )
        ut.lmake( 'max_stderr_len.2'           , done=rc     , steady=1-rc , failed=0  , new=0    , early_rerun=rc  ,            rc=0  )
        ut.lmake( 'stderr_ok.no'               , done=0      , steady=0    , failed=1  , new=0    , early_rerun=rc  ,            rc=1  )
        ut.lmake( 'stderr_ok.yes'              , done=1-rc   , steady=0    , failed=rc , new=0    , early_rerun=rc  ,            rc=rc )
        ut.lmake( 'auto_mkdir.no.ok'           , done=2-rc*2 , steady=0    , failed=rc , new=rc   , early_rerun=rc  ,            rc=rc )
        ut.lmake( 'auto_mkdir.yes.ok'          , done=2-rc*2 , steady=0    , failed=rc , new=rc   , early_rerun=rc  ,            rc=rc )
        ut.lmake( 'cmd'                        , done=1-rc   , steady=0    , failed=rc , new=0    , early_rerun=... ,            rc=rc ) # python3.6 accesses file <code> to report backtrace, ...
        #                                                                                                                                # ... pytho3.12 does not
        for ad in lmake.autodeps : ut.lmake( f'autodep.{ad}.ok' , done=2-rc*2 , steady=0 , failed=rc , new=rc , early_rerun=rc , rc=rc )