Skip to content

[AIMIGRAPHX-1149] Fix prop scope#289

Merged
pfultz2 merged 7 commits into
developfrom
fix_prop_scope
Jul 14, 2026
Merged

[AIMIGRAPHX-1149] Fix prop scope#289
pfultz2 merged 7 commits into
developfrom
fix_prop_scope

Conversation

@eddieliao

Copy link
Copy Markdown
Contributor

Motivation

Ensure properties of tests in subdirectories copy into the final call at root.

Technical Details

(Explanation generated by Opus)
To read a test's properties you must be in the directory scope where the test was registered. Two constraints force the split:

Properties are set after rocm_add_test. e.g. test/verify/CMakeLists.txt calls rocm_add_test(...) and then set_tests_properties(... RESOURCE_LOCK gpu). So you can't read them inside rocm_add_test — they don't exist yet. You have to read them at the end of that directory, once all its set_tests_properties have run. That's exactly what cmake_language(DEFER CALL …) does: it schedules a call to run at the end of the current directory, still in that directory's scope.

cmake_language(DEFER CALL …) can only schedule a named command. You can't defer an inline block; you defer a function. So the property-capture logic has to live in its own named function — rocm_save_test_props — to be the deferred target.

And the two halves now run in different scopes and at different times, so they can't be the same function:

rocm_save_test still runs at root, at finalization, to emit the consolidated add_test lines (target genexes like ROCM_INSTALL_DIR resolve fine there).
rocm_save_test_props runs per-subdirectory, at that directory's end, where get_test_property actually sees the test.
A global property (rocm_test_props) bridges the gap: rocm_save_test_props captures the values in the correct scope and stashes them in a scope-independent global; rocm_save_test reads that global at root and writes the set_tests_properties line right after the test's add_test.

Test Plan

Test Result

Submission Checklist

@eddieliao eddieliao self-assigned this Jun 24, 2026
@eddieliao eddieliao changed the title Fix prop scope [AIMIGRAPHX-1149] Fix prop scope Jun 26, 2026
@pfultz2

pfultz2 commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Properties are set after rocm_add_test. e.g. test/verify/CMakeLists.txt calls rocm_add_test(...) and then set_tests_properties(... RESOURCE_LOCK gpu). So you can't read them inside rocm_add_test — they don't exist yet. You have to read them at the end of that directory, once all its set_tests_properties have run.

We dont read the test properties in rocm_add_test, we read then in rocm_save_test which is run at the end of the top-level directory.

That's exactly what cmake_language(DEFER CALL …) does: it schedules a call to run at the end of the current directory, still in that directory's scope.

Thats what rocm_defer does, and it works on older cmake versions. Its already setup to defer rocm_test_install_ctest and it runs the rocm_save_test commands there.

rocm_save_test_props runs per-subdirectory, at that directory's end, where get_test_property actually sees the test.

Its not exactly per-directory. Its being deferred for each test so its ran multiple times. This is probably slow.

@pfultz2

pfultz2 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

So since get_property handles this correctly we should handle it that way and provide a workaround for when it doesnt support it(like how we do for rocm_eval and rocm_defer). We can have a rocm_get_test_property in ROCMUtilities.cmake that wraps this function and on cmake 3.28 or newer it just called get_test_property however for older cmake we will read a global property for the tests.

We can have a function we collect the tests properties in that directory:

function(rocm_test_collect_local_test_props)
  # Runs at end of the scheduling directory, in its scope —
  # so plain get_test_property works on the local tests.
  get_property(tests DIRECTORY PROPERTY TESTS)
  foreach(test IN LISTS tests)
    # Add a list of the properties we want to capture
    foreach(prop IN ITEMS TIMEOUT LABELS WORKING_DIRECTORY DISABLED)
      get_test_property(${test} ${prop} val)
      if(NOT val STREQUAL "NOTFOUND")
        set_property(GLOBAL APPEND PROPERTY ROCM_TEST_ALL_TEST_PROPS "${test}|${prop}|${val}")
      endif()
    endforeach()
  endforeach()
endfunction()

We need to have that function called with rocm_defer. The current implementation probably needs to be updated to handle defering at directory scope(as I think it uses CMAKE_SOURCE_DIR by default). Perhaps opus can add some unit tests for rocm_defer.

Then in rocm_add_test we will call rocm_auto_register_test_props to defer the call by it will only be deferred once per directory:

function(rocm_auto_register_test_props)
  get_property(scheduled DIRECTORY PROPERTY _rocm_test_props_collector_scheduled)
  if(NOT scheduled)
    set_property(DIRECTORY PROPERTY _rocm_test_props_collector_scheduled TRUE)
    rocm_defer(rocm_test_collect_local_test_props)
  endif()
endfunction()

And then implementation of rocm_get_test_property for older cmake:

function(rocm_get_test_property)
  cmake_parse_arguments(ARG "" "DIRECTORY" "" ${ARGN})

  list(LENGTH ARG_UNPARSED_ARGUMENTS _n)
  if(NOT _n EQUAL 3)
    message(FATAL_ERROR
      "rocm_get_test_property: expected <test> <property> [DIRECTORY <dir>] <out-var>")
  endif()
  list(GET ARG_UNPARSED_ARGUMENTS 0 _test)
  list(GET ARG_UNPARSED_ARGUMENTS 1 _prop)
  list(GET ARG_UNPARSED_ARGUMENTS 2 _out)

  # No DIRECTORY: behave exactly like the builtin (current scope, any property).
  if(NOT DEFINED ARG_DIRECTORY)
    get_test_property(${_test} ${_prop} _val)
    set(${_out} "${_val}" PARENT_SCOPE)
    return()
  endif()

  # Normalize like the builtin does: relative paths are relative to the source dir.
  get_filename_component(_dir "${ARG_DIRECTORY}" ABSOLUTE)

  # If it resolves to the current dir, the builtin already works here — and this
  # dir's cache isn't populated until it finishes processing.
  if(_dir STREQUAL CMAKE_CURRENT_SOURCE_DIR)
    get_test_property(${_test} ${_prop} _val)
    set(${_out} "${_val}" PARENT_SCOPE)
    return()
  endif()

  # Cross-directory: read what the collector stored for that scope.
  set(_key "__tp|${_dir}|${_test}|${_prop}")
  get_property(_set GLOBAL PROPERTY "${_key}" SET)
  if(_set)
    get_property(_val GLOBAL PROPERTY "${_key}")
  else()
    set(_val "NOTFOUND")
  endif()
  set(${_out} "${_val}" PARENT_SCOPE)
endfunction()

This a rough implementation I had claude implement. These three function would reside in ROCMUtilities.cmake.

Comment thread share/rocmcmakebuildtools/cmake/ROCMUtilities.cmake Outdated
@eddieliao
eddieliao requested a review from pfultz2 July 14, 2026 18:29
@pfultz2
pfultz2 enabled auto-merge (squash) July 14, 2026 20:54
@pfultz2
pfultz2 disabled auto-merge July 14, 2026 20:54
@pfultz2
pfultz2 merged commit 6a7c5b7 into develop Jul 14, 2026
18 checks passed
@pfultz2
pfultz2 deleted the fix_prop_scope branch July 14, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants