Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
All NOTABLE changes to this project will be documented in this file.
Check the release summary for a detailed history based on commits.

## [Unreleased]
### Added
- `list-deps` sub-command that enumerates the transitive source files by running parse + import resolution only. Suitable for fast dependency tracking from build systems (e.g. CMake configure-time staleness checks). Writes a plain list (one path per line) to `--file-list <path>` or stdout.

### Changed
- `compile --file-list` now refreshes the dependency manifest immediately after parsing succeeds (instead of after codegen). Failed compiles (frontend or backend errors) still update the manifest, so build systems can pick up newly added imports without requiring a successful build.

## [1.2.0] - 2025-11-09
### Added
- MacOS (ARM64) support
Expand Down
47 changes: 47 additions & 0 deletions build/cmake/test_helper.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,50 @@ function(add_kanagawa_verilator_test name)
endif()
endfunction()


# Helper function for tests that run a command and compare a file it produces
# against a checked-in golden.
#
# The golden is expanded with configure_file-style @VAR@ substitution and then
# converted to native path separators, so a golden can reference absolute
# locations portably via @KANAGAWA_SOURCE_DIR@ (the source tree with symlinks
# resolved, matching the canonical paths the compiler reports).
#
# Usage:
# add_golden_test(<test_name>
# COMMAND <cmd> [<arg> ...] # command that writes ACTUAL
# ACTUAL <file> # file produced by COMMAND
# GOLDEN <file> # golden template, relative to the current source dir
# )
#
# Adds <test_name>.run and <test_name>.golden CTests, chained via a fixture so
# the comparison only runs after the command succeeds.
function(add_golden_test test_name)
set(_one ACTUAL GOLDEN)
set(_multi COMMAND)
cmake_parse_arguments(_ARG "" "${_one}" "${_multi}" ${ARGN})

foreach(_required IN ITEMS COMMAND ACTUAL GOLDEN)
if(NOT _ARG_${_required})
message(FATAL_ERROR "add_golden_test(${test_name}): ${_required} is required.")
endif()
endforeach()

get_filename_component(KANAGAWA_SOURCE_DIR "${CMAKE_SOURCE_DIR}" REALPATH)

set(_golden "${CMAKE_CURRENT_BINARY_DIR}/${test_name}.golden")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/${_ARG_GOLDEN}" _text)
string(CONFIGURE "${_text}" _text @ONLY)
file(TO_NATIVE_PATH "${_text}" _text)
file(WRITE "${_golden}" "${_text}")

add_test(NAME ${test_name}.run COMMAND ${_ARG_COMMAND})
set_tests_properties(${test_name}.run PROPERTIES FIXTURES_SETUP ${test_name})

add_test(
NAME ${test_name}.golden
COMMAND ${CMAKE_COMMAND} -E compare_files --ignore-eol "${_ARG_ACTUAL}" "${_golden}"
)
set_tests_properties(${test_name}.golden PROPERTIES FIXTURES_REQUIRED ${test_name})
endfunction()

59 changes: 45 additions & 14 deletions compiler/hs/app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Language.Kanagawa.Parser.Syntax
import Language.Kanagawa.PrettyPrint
import Language.Kanagawa.Type
import Language.Kanagawa.Warning
import Options (Layout(Pretty, Smart), Options(Compile, PrettyPrint))
import Options (Layout(Pretty, Smart), Options(Compile, ListDeps, PrettyPrint))
import Options.CmdArgs
import qualified Options as O
import ParseTree
Expand Down Expand Up @@ -73,6 +73,10 @@ handle opt@Compile{..} cmdArgs = do
if not $ null parseErrors
then exitErrors parseErrors
else do
-- Refresh the dependency manifest as soon as parsing succeeds, so
-- that it is up to date even when the frontend or codegen fails.
when (not (null file_list) && not (null fileNames)) $
writeIfChanged file_list $ renderDeps fileNames
let desugared = foldr1 append $ frontend passes template_passes template_iterations exprs
when dump_parse $
forM_ exprs $ print . prettyExp
Expand All @@ -95,32 +99,49 @@ handle opt@Compile{..} cmdArgs = do
exitError "Error 1: Warnings treated as errors"
hFlush stdout
success <- compile opt cmdArgs fileNames program
when (not (null file_list) && not (null fileNames)) $
updateFileList file_list $ filter (('.' /=) . head) $ sort fileNames
if success
then exitSuccess
else exitFailure

append (NotedExp _ (SeqF x)) (NotedExp n (SeqF y)) = NotedExp n (SeqF (x ++ y))
append _ _ = undefined

-- Enumerate the transitive set of source files needed to compile the program.
-- Runs parse + import resolution only, skipping the frontend and codegen.
handle opt@ListDeps{..} _ = do
parsedFiles <- flip execStateT [] $ parseProgram $ getParseOptions opt
let (fileNames, results) = unzip parsedFiles
parseErrors = lefts results
if not $ null parseErrors
then exitErrors parseErrors
else do
let deps = renderDeps fileNames
if null file_list
then putStr deps
else writeIfChanged file_list deps
exitSuccess

-- Run languange server
--handle LangServer{..} = runLangServer log_file

-- Print usage
handle opt _ = print opt

updateFileList :: FilePath -> [FilePath] -> IO ()
updateFileList listfile parsedFiles = do
old <- lines <$> listfileContent
when (old /= parsedFiles) $
writeFile listfile $ unlines parsedFiles
where
listfileContent = do
exists <- doesFileExist listfile
if exists
then T.unpack <$> TIO.readFile listfile
else return ""
-- | One sorted path per line, excluding internal synthetic modules whose
-- names start with '.' (e.g. .cmdargs.k, .options.k).
renderDeps :: [FilePath] -> String
renderDeps = unlines . filter (('.' /=) . head) . sort

-- | Write @content@ to @path@, leaving the file (and its mtime) untouched if
-- it already has those contents.
writeIfChanged :: FilePath -> String -> IO ()
writeIfChanged path content = do
exists <- doesFileExist path
old <- if exists
then T.unpack <$> TIO.readFile path
else return ""
when (old /= content) $
writeFile path content

getParseOptions :: Options -> ParseOptions
getParseOptions opt = case opt of
Expand Down Expand Up @@ -149,4 +170,14 @@ getParseOptions opt = case opt of
, targetDevice = target_device
, using = using
}
ListDeps{..} -> defaultOptions
{ baseLibrary = base_library
, define = define
, files = files
, importDir = import_dir
, noImplicitBase = no_implicit_base
, parseDocs = parse_docs
, targetDevice = target_device
, using = using
}
_ -> error "Unsupported mode."
11 changes: 11 additions & 0 deletions compiler/hs/app/Options.hs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ data Options
, write_ir_post_opt :: Bool
, skip_circt_lowering :: Bool
}
| ListDeps
{ base_library :: FilePath
, define :: [String]
, file_list :: FilePath
, files :: [FilePath]
, import_dir :: [FilePath]
, no_implicit_base :: Bool
, parse_docs :: Bool
, target_device :: String
, using :: [String]
}
| LangServer
{ verbose :: Bool
, log_file :: Maybe String
Expand Down
13 changes: 12 additions & 1 deletion compiler/hs/app/Options/CmdArgs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,19 @@ pretty = PrettyPrint
name "pretty" &=
help "Parse and pretty print source file(s)"

listDeps :: Options
listDeps = ListDeps{} &=
name "list-deps" &=
help "List the transitive .k source files needed to compile (parse only; no frontend or codegen)" &=
details [ "Usage example:"
, " Print the dependency list for source.k to stdout:"
, " kanagawa list-deps source.k"
, " Write the dependency list to a file:"
, " kanagawa list-deps --file-list=deps.txt source.k"
]

mode :: Mode (CmdArgs Options)
mode = cmdArgsMode $ modes [compile &= auto, pretty] &=
mode = cmdArgsMode $ modes [compile &= auto, pretty, listDeps] &=
program "kanagawa" &=
verbosity &=
help "Kanagawa compiler" &=
Expand Down
38 changes: 38 additions & 0 deletions test/compiler/cli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,41 @@ add_cli_test(skip_circt_lowering
TEST
"python3 ${CMAKE_CURRENT_SOURCE_DIR}/check_skip_circt_lowering.py ${CMAKE_CURRENT_BINARY_DIR}/skip_circt_lowering"
)

# `list-deps` resolves imports without running the frontend or codegen, so it
# does not fit add_cli_test's compile-and-inspect shape.
add_golden_test(cli.list_deps
COMMAND $<TARGET_FILE:kanagawa::exe> list-deps
--base-library=${CMAKE_SOURCE_DIR}/library/mini-base.k
--import-dir=${CMAKE_SOURCE_DIR}/library
--file-list=${CMAKE_CURRENT_BINARY_DIR}/list_deps.txt
${CMAKE_CURRENT_SOURCE_DIR}/list_deps.k
ACTUAL ${CMAKE_CURRENT_BINARY_DIR}/list_deps.txt
GOLDEN list_deps.golden.in
)

# Generate the same file list via compile --file-list
add_test(
NAME cli.list_deps.compile_file_list
COMMAND $<TARGET_FILE:kanagawa::exe> compile
--base-library=${CMAKE_SOURCE_DIR}/library/mini-base.k
--import-dir=${CMAKE_SOURCE_DIR}/library
--file-list=${CMAKE_CURRENT_BINARY_DIR}/compile_file_list.txt
--output=${CMAKE_CURRENT_BINARY_DIR}/list_deps_compile
${CMAKE_CURRENT_SOURCE_DIR}/list_deps.k
)
set_tests_properties(cli.list_deps.compile_file_list PROPERTIES
FIXTURES_REQUIRED cli.list_deps
FIXTURES_SETUP cli.list_deps.compile_file_list
)

# Verify that `list-deps` and `compile --file-list` produce the same output
add_test(
NAME cli.list_deps.compare_file_lists
COMMAND ${CMAKE_COMMAND} -E compare_files
${CMAKE_CURRENT_BINARY_DIR}/list_deps.txt
${CMAKE_CURRENT_BINARY_DIR}/compile_file_list.txt
)
set_tests_properties(cli.list_deps.compare_file_lists PROPERTIES
FIXTURES_REQUIRED cli.list_deps.compile_file_list
)
9 changes: 9 additions & 0 deletions test/compiler/cli/list_deps.golden.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@KANAGAWA_SOURCE_DIR@/library/compiler/config.k
@KANAGAWA_SOURCE_DIR@/library/compiler/device/config.k
@KANAGAWA_SOURCE_DIR@/library/compiler/device/schema.k
@KANAGAWA_SOURCE_DIR@/library/data/closure.k
@KANAGAWA_SOURCE_DIR@/library/data/closure/core.k
@KANAGAWA_SOURCE_DIR@/library/debug/print.k
@KANAGAWA_SOURCE_DIR@/library/device/mock/hardware/config.k
@KANAGAWA_SOURCE_DIR@/library/mini-base.k
@KANAGAWA_SOURCE_DIR@/test/compiler/cli/list_deps.k
16 changes: 16 additions & 0 deletions test/compiler/cli/list_deps.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Importing `data.closure` (which itself imports `data.closure.core`) makes
// the golden manifest cover transitively imported modules.
import data.closure

class Main
{
public:
void main()
{
}
}

export Main;
Loading