Reference implementations of PQC
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_makefile_dependencies.py 3.5 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. """
  2. Checks that every .c and .h file in an implementation is present as a
  3. dependency of that scheme's Makefile.
  4. """
  5. import datetime
  6. import glob
  7. import os
  8. import pytest
  9. import helpers
  10. import pqclean
  11. @pytest.mark.parametrize(
  12. 'implementation,test_dir,impl_path, init, destr',
  13. [(impl,
  14. *helpers.isolate_test_files(impl.path(), 'test_makefile_deps_'))
  15. for impl in pqclean.Scheme.all_supported_implementations()],
  16. ids=[str(impl) for impl in pqclean.Scheme.all_supported_implementations()],
  17. )
  18. @helpers.filtered_test
  19. def test_makefile_dependencies(implementation, impl_path, test_dir,
  20. init, destr):
  21. init()
  22. # initial build - want to have *all* files in place at beginning
  23. helpers.make('clean', working_dir=impl_path)
  24. helpers.make(working_dir=impl_path)
  25. # test case for each candidate file
  26. cfiles = glob.glob(os.path.join(impl_path, '*.c'))
  27. sfiles = glob.glob(os.path.join(impl_path, '*.[sS]'))
  28. incfiles = glob.glob(os.path.join(impl_path, '*.inc'))
  29. hfiles = glob.glob(os.path.join(impl_path, '*.h'))
  30. try:
  31. for file in (cfiles + hfiles + sfiles + incfiles):
  32. check_makefile_dependencies(implementation, impl_path, file)
  33. except Exception as e:
  34. print("Affected file: {}".format(file))
  35. raise e
  36. finally:
  37. destr()
  38. def touch(time, *files):
  39. for path in files:
  40. times = (time.timestamp(), time.timestamp())
  41. os.utime(path, times)
  42. def make_check(path, expect_error=False):
  43. makeflag = '-q' if os.name != 'nt' else '/Q'
  44. expected_returncode = 0
  45. if expect_error:
  46. expected_returncode = 1 if os.name != 'nt' else 255
  47. helpers.make(makeflag, working_dir=path,
  48. expected_returncode=expected_returncode)
  49. def check_makefile_dependencies(implementation, impl_path, file):
  50. cfiles = glob.glob(os.path.join(impl_path, '*.c'))
  51. sfiles = glob.glob(os.path.join(impl_path, '*.[sS]'))
  52. hfiles = glob.glob(os.path.join(impl_path, '*.h'))
  53. incfiles = glob.glob(os.path.join(impl_path, '*.inc'))
  54. o_ext = '*.o' if os.name != 'nt' else '*.obj'
  55. ofiles = glob.glob(os.path.join(impl_path, o_ext))
  56. # handle dependency files: these also need to be set correctly
  57. commondir = os.path.join(impl_path, '..', '..', '..', 'common')
  58. cfiles += glob.glob(os.path.join(commondir, '*.c'))
  59. cfiles += glob.glob(os.path.join(commondir, '**', '*.c'))
  60. hfiles += glob.glob(os.path.join(commondir, '*.h'))
  61. hfiles += glob.glob(os.path.join(commondir, '**', '*.h'))
  62. incfiles.append(os.path.join(commondir, 'keccak4x', 'KeccakP-1600-unrolling.macros'))
  63. ofiles += glob.glob(os.path.join(commondir, o_ext))
  64. ofiles += glob.glob(os.path.join(commondir, '**', o_ext))
  65. libfile = os.path.join(impl_path, implementation.libname())
  66. # modification time-based calculations is tricky on a sub-second basis
  67. # so we reset all the modification times to a known and "sensible" order
  68. now = datetime.datetime.now() - datetime.timedelta(seconds=10)
  69. ago15 = now - datetime.timedelta(minutes=15)
  70. ago10 = now - datetime.timedelta(minutes=10)
  71. ago5 = now - datetime.timedelta(minutes=5)
  72. touch(ago15, *cfiles, *sfiles, *hfiles, *incfiles)
  73. touch(ago10, *ofiles)
  74. touch(ago5, libfile)
  75. # Sanity check: the scheme is up to date
  76. make_check(impl_path)
  77. # touch the candidate .c / .h file
  78. touch(now, file)
  79. # check if it needs to be rebuilt using make -q
  80. make_check(impl_path, expect_error=True)
  81. if __name__ == '__main__':
  82. import sys
  83. pytest.main(sys.argv)